Layer Image Synthesizer â Extensible Multi-EFX Engineã¬ã€ã€ãŒã€ã¡ãŒãžã·ã³ã»ãµã€ã¶ãŒ â æ¡åŒµåãã«ãEFXãšã³ãžã³
Part of mederu Ateliermederu Atelier ã®äžéš
"The universe is made of many layers." ãå®å®ã¯ããã€ãã®ã¬ã€ã€ãŒã§ã§ããŠããã
me:DERU is a Layer Image Synthesizer that composes all image EFX by stacking layers. Multiple layers are combined with a rich variety of blend parameters, and effects are stacked upon each other. Drag & Drop to reorder layers, add MOD EFX â building images like a synthesizer builds sound. me:DERU ã¯ç»åEFXãã¹ãŠãéãåãããŠæ§æããã¬ã€ã€ãŒã€ã¡ãŒãžã·ã³ã»ãµã€ã¶ãŒã§ããè€æ°ã®ã¬ã€ã€ãŒãéãåããããçš®é¡è±å¯ãªç»ååæãã©ã¡ãŒã¿ããšãã§ã¯ãã幟éã«ãéãåããããD&Dã§ã¬ã€ã€ãŒãäžäžãMOD EFXã远å â ã·ã³ã»ãµã€ã¶ãŒãé³ãæ§ç¯ããããã«ãç»åãæ§ç¯ããŸãã
me:DERU is an extensible multi-EFX engine with two processing modes â pick the one that fits your workflow. me:DERU ã¯æ¡åŒµåãã«ãEFXãšã³ãžã³ã§ãã2ã€ã®æ¹åŒã§ç»åãå å·¥ã§ããŸãã
| Modeã¢ãŒã | Styleã¹ã¿ã€ã« | |
|---|---|---|
| ðš | DERU | Layer-based compositing â linear, intuitiveã¬ã€ã€ãŒåæ â çŽç·çãçŽæç |
| ð§© | NODE | Node graph pipeline â flexible, powerfulããŒãã°ã©ã â èªç±åºŠãé«ãã匷å |
flowchart LR
DRAW["âïž DRAW\n(velvet.html)"] -->|ðž Snapshot| LIB["ðž LIBRARY\n(IndexedDB)"]
subgraph MEDERU["ð® me:DERU"]
DERU["ðš DERU\nLayer Compositing"]
NODE["ð§© NODE\nGraph Pipeline"]
end
LIB -->|"Select image"| DERU
LIB -->|"Select / L key"| NODE
DERU <-->|"Shared LIBRARY"| NODE
DERU -->|Export| POST["ð® POST\nMint / Share"]
NODE -->|"S key / Output"| POST
me:DERU is the extensible multi-EFX engine within mederu Atelier. It offers two modes â DERU for intuitive layer-based compositing, and NODE for flexible graph-based pipelines. Both share the same LIBRARY and EFX modules, which are fully plugin-based.me:DERU 㯠mederu Atelier ã®æ¡åŒµåãã«ãEFXãšã³ãžã³ã§ããDERUïŒçŽæçãªã¬ã€ã€ãŒåæïŒãš NODEïŒèªç±åºŠã®é«ãã°ã©ããã€ãã©ã€ã³ïŒã®2ã€ã®ã¢ãŒããæäŸããŸããLIBRARYãšEFXã¢ãžã¥ãŒã«ã¯å ±éã§ããã©ã°ã€ã³æ¹åŒã§æ¡åŒµå¯èœã§ãã
DERU mode is a layer-based image compositing engine. Stack multiple images as layers, apply EFX chains to each, and composite them together.DERU ã¢ãŒãã¯ã¬ã€ã€ãŒããŒã¹ã®ç»ååæãšã³ãžã³ã§ããè€æ°ã®ç»åãã¬ã€ã€ãŒãšããŠéããåã¬ã€ã€ãŒã« EFX ãã§ãŒã³ãé©çšããŠåæããŸãã
flowchart TD
A["ðž Select from LIBRARY"] --> B["ð Add as Layer"]
B --> C["ðïž Add EFX Chain"]
C --> D["Adjust Parameters"]
D --> E["ð Real-time Preview"]
E --> F{"Satisfied?"}
F -->|No| C
F -->|Yes| G["ð€ Export â POST"]
| Tab | Description説æ |
|---|---|
| ðïž LAYERS | Layer compositing mode â stack images and apply EFXã¬ã€ã€ãŒåæã¢ãŒããç»åãéããŠEFXé©çš |
| ðïž GIF | GIF animation creation modeGIFã¢ãã¡ãŒã·ã§ã³äœæã¢ãŒã |
| Keyã㌠| Actionæ©èœ |
|---|---|
| H | Toggle help panelãã«ããã㫠衚瀺/é衚瀺 |
| Space | [GIF] Play / Pause animation[GIF] åç/äžæåæ¢ |
| Delete / â« | [GIF] Remove current frame[GIF] çŸåšã®ãã¬ãŒã åé€ |
| Escape | Close help / modalsãã«ã/ã¢ãŒãã«ãéãã |
| Actionæäœ | Methodæ¹æ³ |
|---|---|
| Add layerã¬ã€ã€ãŒè¿œå | Click LIBRARY thumbnail or â Add LayerLIBRARY ã®ç»åã¯ãªã㯠or â Add Layer |
| Select layerã¬ã€ã€ãŒéžæ | Click layer rowã¬ã€ã€ãŒè¡ãã¯ãªã㯠|
| Reorder layersã¬ã€ã€ãŒäžŠã¹æ¿ã | Drag â®â® handle to reorder (D&D)â®â® ãã³ãã«ããã©ãã°ããŠäžŠã¹æ¿ãïŒD&DïŒ |
| Show / Hide衚瀺/é衚瀺 | ðïž / ð« |
| Delete layerã¬ã€ã€ãŒåé€ | ðïž Delete Layer |
| Collapse sectionã»ã¯ã·ã§ã³æãããã¿ | Click section titleã»ã¯ã·ã§ã³ã¿ã€ãã«ã¯ãªã㯠|
| Parameterãã©ã¡ãŒã¿ | Description説æ |
|---|---|
| Opacity | 0â100% |
| Blend Mode | normal · multiply · screen · overlay · difference · color-dodge · color-burn · hard-light · soft-light |
| Actionæäœ | Methodæ¹æ³ |
|---|---|
| Add as layerã¬ã€ã€ãŒãšããŠè¿œå | Click thumbnailãµã ãã€ã«ã¯ãªã㯠|
| Delete individualåå¥åé€ | Hover â â buttonãµã ãã€ã«ã«ãã㌠â â |
| Clear allå šåé€ | ðïž Clear All (shown when 2+ frames)ðïž Clear All (2å以äžã§è¡šç€º) |
| Import imagesç»åã€ã³ããŒã | ð Import |
| Collapseæãããã¿ | Click titleã¿ã€ãã«ã¯ãªã㯠|
Apply effects as a chain to the selected layer. Effects are processed in order.éžæäžã®ã¬ã€ã€ãŒã«å¯ŸããŠãšãã§ã¯ãããã§ãŒã³ïŒé£éïŒã§é©çšã§ããŸãã
flowchart LR
SRC["Source Image"] --> E1["EFX 1\nColor Adjust"]
E1 --> E2["EFX 2\nMelt"]
E2 --> E3["EFX 3\nNoise"]
E3 --> OUT["Output"]
| Actionæäœ | Methodæ¹æ³ |
|---|---|
| Add EFXEFX 远å | â Add Effect |
| Remove EFXEFX åé€ | â |
| Toggle ON/OFFEFX ON/OFF | Click EFX nameEFXåã¯ãªã㯠|
| Actionæäœ | Methodæ¹æ³ |
|---|---|
| Select framesãã¬ãŒã éžæ | Click LIBRARY thumbnails (checkmark)LIBRARYã®ãµã ãã€ã«ãã¯ãªã㯠|
| Add to timelineã¿ã€ã ã©ã€ã³ã«è¿œå | â Add Selected |
| Frame delayãã¬ãŒã é å»¶ | Delay slider per frameåãã¬ãŒã ã® delay ã¹ã©ã€ã㌠|
| Play directionåçæ¹å | forward / reverse / pingpong |
Generate GIF animations from a single image automatically:åäžç»åããGIFã¢ãã¡ãŒã·ã§ã³ãèªåçæïŒ
| Presetããªã»ãã | Icon | Description説æ |
|---|---|---|
| Wave H | ð | Horizontal wave distortionæ°Žå¹³æ¹åã®æ³¢åœ¢å€åœ¢ |
| Wave V | ð | Vertical wave distortionåçŽæ¹åã®æ³¢åœ¢å€åœ¢ |
| Squishy | ð« | Squish & stretch bounce䌞ã³çž®ã¿ããŠã³ã¹ |
| 3D Spin | ð | Pseudo-3D card flipç䌌3Dã«ãŒãå転 |
| Spiral | ð | Rotate + scale spiralå転+ã¹ã±ãŒã«ã¹ãã€ã©ã« |
| Bounce | ⬠| Vertical bouncingåçŽããŠã³ã¹ |
| Trail | ð» | Ghost trail effectãŽãŒã¹ããã¬ã€ã« |
| Pulse | ð« | Pulsing scaleèåã¹ã±ãŒã« |
Parameters: Amplitude (strength), Frequency (cycles), Speedãã©ã¡ãŒã¿: AmplitudeïŒåŒ·åºŠïŒ, FrequencyïŒãµã€ã¯ã«æ°ïŒ, SpeedïŒé床ïŒ
NODE mode is a node-graph-based image processing pipeline. Connect nodes with wires to build your processing flow.NODE ã¢ãŒãã¯ããŒãã°ã©ãããŒã¹ã®ç»ååŠçãã€ãã©ã€ã³ã§ããããŒãå士ãç·ã§ç¹ãã§åŠçãããŒãæ§ç¯ããŸãã
flowchart TD
A["Tab / A to open palette"] --> B["Add nodes"]
B --> C["Connect ports with wires"]
C --> D["Load image: D&D / click / L key"]
D --> E["Adjust parameters"]
E --> F{"Auto mode?"}
F -->|Yes| G["ð Auto-execute\n600ms debounce"]
F -->|No| H["E / Enter to execute"]
G --> I["Preview updates"]
H --> I
I --> J["S to send to POST"]
â» Case-insensitive â works with CapsLock ON⻠倧æåã»å°æåã©ã¡ãã§ãåäœããŸã (CapsLock OK)
| Keyã㌠| Actionæ©èœ |
|---|---|
| H | Toggle help panelãã«ããã㫠衚瀺/é衚瀺 |
| Tab / A | Toggle Add Node paletteAdd Node ãã¬ãã 衚瀺/é衚瀺 |
| L | Toggle LIBRARY popup (select Source image)LIBRARY ãããã¢ããïŒSourceç»åéžæïŒ |
| E / Enter | Execute graph pipelineã°ã©ãå®è¡ |
| S | Save â export output to POSTSave â åºåç»åã POST ã«éã |
| Delete / â« | Delete selected node or edgeéžæäžã®ããŒã/ãšããžãåé€ |
| Escape | Close all panels / deselectå šããã«ãéãã / éžæè§£é€ |
| F | Fit view (zoom to show all nodes)ãã¥ãŒããã£ãã |
| Actionæäœ | Methodæ¹æ³ |
|---|---|
| Move nodeããŒãç§»å | Drag nodeããŒãããã©ãã° |
| Connectæ¥ç¶ | Drag port â â port âããŒãâããããŒãâãžãã©ãã° |
| Select edgeãšããžéžæ | Click wireç·ãã¯ãªã㯠|
| Load imageç»åèªèŸŒ | D&D / click Source / L for LIBRARYD&D / ã¯ãªã㯠/ L ã§LIBRARY |
| ZoomãºãŒã | Mouse wheelããŠã¹ãã€ãŒã« |
| Panãã³ | Drag backgroundèæ¯ããã©ãã° |
| Zoom previewãã¬ãã¥ãŒæ¡å€§ | Click EFX node preview imageEFXããŒãã®ãã¬ãã¥ãŒç»åãã¯ãªã㯠|
flowchart LR
subgraph UTILITY["ð§ UTILITY"]
SRC["ðŒïž Source\nImage Input"]
BLD["ð Blend\nCompositing"]
OUT["ð€ Output\nFinal Result"]
end
subgraph EFX["ðïž EFX"]
FLT["ð£ Filter"]
DST["ðµ Distortion"]
GEN["ð¡ Generative"]
end
SRC --> FLT --> DST --> OUT
SRC --> BLD --> OUT
| Node | Colorè² | Description説æ |
|---|---|---|
| ðŒïž Source | ð¢ | Image input â D&D, click, or L keyç»åå ¥åãD&D / ã¯ãªã㯠/ L ã§LIBRARY |
| ð Blend | ð | Mix 2 inputs (multiply, screen, overlayâŠ)2ã€ã®å ¥åãåæ |
| ð€ Output | ðŽ | Final output â â¶ fullscreen previewæçµåºåå ãâ¶ ã§å šç»é¢ãã¬ãã¥ãŒ |
| Module | Icon | Parameters | Description説æ |
|---|---|---|---|
| Color Adjust | ðš | Brightness, Contrast, Saturation | Color correctionè²èª¿è£æ£ |
| Invert | ð | â | Color inversion (negative)è²å転ïŒãã¬ïŒ |
| Posterize | ðŒïž | Levels | Posterizationãã¹ã¿ãªãŒãŒã·ã§ã³ |
| Threshold | â« | Level | Black & white binarizeçœé»äºå€å |
| Duotone | ð | Shadow, Highlight | Dual-tone mappingããã«ããŒã³ |
| Gradient Map | ð | Shadow, Mid, Highlight | Gradient mapã°ã©ããŒã·ã§ã³ããã |
| Lo-Fi | ðŒ | Pixelate, Noise, Scanlines | Retro / VHS effectã¬ãã颚å å·¥ |
| Color Shift | ðš | Intensity | Random hue shiftã©ã³ãã è²çžã·ãã |
| Line Art | âïž | Threshold, Thickness | Line extractionç·ç»æœåº |
| ASCII Art | ð€ | Size, Density | ASCII art conversionASCIIã¢ãŒã倿 |
| Line Edge | âïž | Threshold | Edge detectionãšããžæ€åº |
| Text Overlay | ð | Text, Font, Size, Color, Position, Rotation | Add text overlay on imageç»åäžã«ããã¹ããªãŒããŒã¬ã€ |
| Text Frame | ðŒïž | Text, Font, Size, Border Color, Padding | Text frame / border with typographyã¿ã€ãã°ã©ãã£ä»ãããã¹ããã¬ãŒã |
| Module | Icon | Parameters | Description説æ |
|---|---|---|---|
| Melt | ð« | Intensity, Direction, Drip | Melt / dissolveã¡ã«ãïŒæº¶è§£ïŒ |
| Transform | ð | Scale, Rotate, X, Y | Scale / rotate / translateå€åœ¢ |
| Feedback | âŸïž | Intensity, Zoom, Rotate | Feedback loopãã£ãŒããã㯠|
| RGB Split | ðŽð¢ðµ | Red X/Y, Blue X/Y | RGB channel separationRGBãã£ã³ãã«åé¢ |
| RGB Glitch | ð | Intensity | Simple RGB glitchç°¡æRGBã°ãªãã |
| Slit Scan | ð | Intensity | Slit scanã¹ãªããã¹ãã£ã³ |
| Portrait Distort | ð | Intensity, SliceWidth, Freq | Vertical slice distortion瞊ã¹ã©ã€ã¹æªã¿ |
| Horizontal Distort | ð | Intensity, SliceHeight, Freq | Horizontal slice distortion暪ã¹ã©ã€ã¹æªã¿ |
| Fragment Glitch | ð¥ | Intensity, BlockSize, Randomness | Fragment glitchãã©ã°ã¡ã³ãã°ãªãã |
| Module | Icon | Parameters | Description説æ |
|---|---|---|---|
| Noise | ðº | Level | Add noiseãã€ãºä»å |
| Dither ð | ð² | Mode, Scale, Color Mode | Dithering (Floyd-Steinberg, Bayer, etc.) â NFT Pass requiredãã£ã¶ãªã³ã°ïŒFloyd-SteinbergãBayerçïŒ â NFTãã¹å¿ é |
| Kanji Art ð | ãïž | Density, Font Size, Color Mode | Convert image to Kanji characters â NFT Pass requiredç»åãæŒ¢åæåã«å€æ â NFTãã¹å¿ é |
| Modeã¢ãŒã | Buttonãã¿ã³ | Behavioråäœ |
|---|---|---|
| Auto | ð Auto | Auto-execute 600ms after parameter changeãã©ã¡ãŒã¿å€æŽåŸ 600ms ã§èªåå®è¡ |
| Manual | âž Manual | Press E / Enter to execute manuallyE / Enter ããŒã§æåå®è¡ |
flowchart TD
subgraph Pipeline["Execution Pipeline"]
direction TB
S1["ðŒïž Source 1"] --> E1["ðš Color Adjust"]
E1 --> E2["ð« Melt"]
S2["ðŒïž Source 2"] --> BL["ð Blend"]
E2 --> BL
BL --> E3["ðº Noise"]
E3 --> OUT["ð€ Output"]
end
| ðš me:DERU | ð§© NODE | |
|---|---|---|
| Workflowæäœæ | Linear (top â down)çŽç·ç | Free graph (node wiring)èªç±ã°ã©ã |
| Layersã¬ã€ã€ãŒ | â | â via Blend nodeâ BlendããŒãã§ |
| EFX ChainEFXãã§ãŒã³ | Per-layer chainã¬ã€ã€ãŒããš | Free connectionsèªç±æ¥ç¶ |
| Previewãã¬ãã¥ãŒ | Real-time autoãªã¢ã«ã¿ã€ã | Auto / Manual toggleAuto / Manual |
| GIFGIFäœæ | â Timeline + Auto-Motionâ | â |
| LIBRARY | Always visible in sidebarãµã€ãããŒã«åžžæè¡šç€º | L key popupL ããŒã§ãããã¢ãã |
| Best foråããŠããçšé | Quick compositingã·ã³ãã«ãªåæ | Complex pipelinesè€éãªãã€ãã©ã€ã³ |
me:DERU's effects are built on a MOD (Module) system. Like Atelier's materials, each MOD contains artist-created code â a magic box full of possibilities. MODs are plug-and-play: create one function, and it works in both DERU and NODE editors.me:DERUã®ãšãã§ã¯ãã¯MODïŒã¢ãžã¥ãŒã«ïŒã·ã¹ãã ã§æ§æãããŠããŸããAtelierã®çŽ æã®ããã«ãåMODã«ã¯ã¢ãŒãã£ã¹ããäœã£ãã³ãŒããè©°ãŸã£ãéæ³ã®ç®±ã§ããMODã¯ãã©ã°ã¢ã³ããã¬ã€ïŒ1ã€ã®é¢æ°ãäœãã°ãDERUãšNODEäž¡æ¹ã®ãšãã£ã¿ã§åäœããŸãã
flowchart LR
SRC["ðŒïž Source\nImage pixels RGBA"] --> MOD["⊠YOUR MOD\nprocessPixels"]
MOD --> OUT["ð€ Output\nModified pixels"]
PARAMS["ðïž Parameters\nSliders / Toggles"] -.-> MOD
| Featureç¹åŸŽ | Description説æ |
|---|---|
| â¡ Pure ProcessingçŽç²ãªåŠç | Your MOD is a single function. Input goes in, output comes out. No DOM, no state, no side effects.MODã¯åäžã®é¢æ°ãå ¥åãå ¥ã£ãŠãåºåãåºããDOMäžèŠãã¹ããŒãäžèŠãå¯äœçšãªãã |
| ðïž Auto Parametersèªåãã©ã¡ãŒã¿ | Define sliders, toggles, and selectors in your params definition â UI is auto-generated.paramså®çŸ©ã§ã¹ã©ã€ããŒããã°ã«ãã»ã¬ã¯ã¿ãŒãå®çŸ© â UIã¯èªåçæã |
| ð DERU & NODE ReadyDERUã»NODEäž¡å¯Ÿå¿ | MODs work in both editors. Create art and MINT it as your output.MODã¯äž¡ãšãã£ã¿ã§åäœãã¢ãŒãå¶äœåŸãMINTããŠåºåã |
| ð NFT TokengateNFTããŒã¯ã³ã²ãŒã | MODs can be sold as NFTs. Owning them unlocks tokengate-protected modules.MODã¯NFTãšããŠè²©å£²å¯èœãææããããšã§ã²ãŒãéå®ã¢ãžã¥ãŒã«ãã¢ã³ããã¯ã |
| ð¬ Video Readyåç»å¯Ÿå¿äºå® | MODs process one frame at a time â inherently ready for GIF/video processing with no code changes.MODã¯1ãã¬ãŒã ãã€åŠç â ã³ãŒã倿Žãªãã§GIF/åç»åŠçã«å¯Ÿå¿äºå®ã |
EFX LAB is a standalone tool for prototyping MODs. Write code, drop an image, and see the result instantly. Parameters are auto-detected from your code (e.g. params.contrast||100 auto-generates a slider).EFX LAB ã¯MODã®ãããã¿ã€ãã³ã°çšã¹ã¿ã³ãã¢ãã³ããŒã«ã§ããã³ãŒããæžããç»åãããããããçµæããªã¢ã«ã¿ã€ã ã§ç¢ºèªããã©ã¡ãŒã¿ã¯ã³ãŒãããèªåæ€åºãããŸãïŒäŸ: params.contrast||100 ã§ã¹ã©ã€ããŒãèªåçæïŒã
The MOD ecosystem continues to grow. Here are planned and in-development MODs:MODãšã³ã·ã¹ãã ã¯æé·ãç¶ããŠããŸããèšç»äžã»éçºäžã®MODã¯ãã¡ãïŒ
| MOD | Categoryã«ããŽãª | Description説æ | Statusç¶æ |
|---|---|---|---|
| ð¹ MIDI | Generative | MIDI-reactive visual effects â parameters respond to MIDI input in real-timeMIDIãªã¢ã¯ãã£ãããžã¥ã¢ã«ãšãã§ã¯ã â ãã©ã¡ãŒã¿ãMIDIå ¥åã«ãªã¢ã«ã¿ã€ã å¿ç | Plannedèšç»äž |
| ðš Nathan | Filter | Artist collaboration MOD â unique visual processing style by Nathanã¢ãŒãã£ã¹ãã³ã©ãMOD â Nathanç¬èªã®ããžã¥ã¢ã«åŠçã¹ã¿ã€ã« | Plannedèšç»äž |
| ð GIU | Distortion | Artist collaboration MOD â GIU's signature distortion and generative effectsã¢ãŒãã£ã¹ãã³ã©ãMOD â GIUç¬èªã®ãã£ã¹ããŒã·ã§ã³ïŒãžã§ãã©ãã£ããšãã§ã¯ã | Plannedèšç»äž |
| ð¬ Video EFXåç»EFX | Core | Frame-by-frame video processing â existing MODs auto-apply to each frameãã¬ãŒã åäœã®åç»åŠç â æ¢åMODãåãã¬ãŒã ã«èªåé©çš | Plannedèšç»äž |
The EFX system is fully plugin-based. You can add a new image effect in 2 files and it will automatically appear in both DERU and NODE editors.EFXã·ã¹ãã ã¯å®å šãªãã©ã°ã€ã³æ¹åŒã§ãã2ãã¡ã€ã«ã®å€æŽã ãã§æ°ãããšãã§ã¯ããDERUãšNODEäž¡æ¹ã«èªåçã«åæ ãããŸãã
flowchart LR
A["1. Create\nmodules/my-effect.ts"] --> B["2. Import + register in\nregistry.ts"]
B --> C["3. Auto-appears in\nDERU 'Add Effect'\n& NODE palette"]
C --> D["4. Parameters auto-generate\nslider / color / select UI"]
// Project structure src/lib/efx/ âââ types.ts // EFXModule, EFXParamDef, Frame, etc. âââ utils.ts // loadImage, dataUrlToCanvas, generateThumbnail âââ registry.ts // EFX_REGISTRY â master list of all modules âââ chain.ts // applyEFXChain â sequential EFX processing âââ modules/ âââ color.ts // Color Adjust, Invert, Posterize, etc. âââ distortion.ts // Melt, Transform, Feedback, etc. âââ glitch.ts // RGB Glitch, Slit Scan, Fragment, etc. âââ ascii.ts // ASCII Art, Line Edge âââ text.ts // Text Overlay, Text Frame âââ dither.ts // Dither (NFT-Gated ð) âââ kanji-art.ts // Kanji Art (NFT-Gated ð) âââ my-effect.ts // â YOUR NEW MODULE
Every module must implement this interface (defined in types.ts):ãã¹ãŠã®ã¢ãžã¥ãŒã«ã¯æ¬¡ã®ã€ã³ã¿ãŒãã§ãŒã¹ãå®è£
ããŸãïŒtypes.tsã§å®çŸ©ïŒ:
interface EFXModule { id: string; // Unique ID (kebab-case) e.g. 'my-sepia' name: string; // Display name e.g. 'Sepia Tone' icon: string; // Emoji icon e.g. 'ð€' category: EFXCategory; // 'filter' | 'distortion' | 'generative' | 'composite' params: EFXParamDef[]; // Parameter definitions â auto UI // REQUIRED: Process a single frame processFrame: ( frame: Frame, // Input image (has .dataUrl) params: Record<string, any>, // User-adjusted parameter values context: EFXContext, // Canvas size, frame index, etc. ) => Promise<ProcessedFrameData>; // Return { dataUrl, thumbnail } // OPTIONAL: Batch processing (for transitions) processBatch?: (frames, params, ctx, onProgress?) => Promise<...>; // OPTIONAL: Low-res fast preview previewFrame?: (frame, params) => Promise<string>; }
Parameters you define in params[] automatically generate the corresponding UI controls:params[]ã«å®çŸ©ãããã©ã¡ãŒã¿ã¯èªåçã«å¯Ÿå¿ããUIã³ã³ãããŒã«ãçæããŸã:
| type | UI | Required Fieldså¿ é ãã£ãŒã«ã | ExampleäŸ |
|---|---|---|---|
range | Slider | min, max, step, default | { key:'amount', label:'Amount', type:'range', default:50, min:0, max:100 } |
color | Color picker | default | { key:'tint', label:'Tint', type:'color', default:'#ff6b9d' } |
select | Dropdown | options[], default | { key:'mode', label:'Mode', type:'select', default:'a', options:[{value:'a',label:'A'}] } |
toggle | Checkbox | default | { key:'on', label:'Enable', type:'toggle', default:true } |
number | Number input | min, max, default | { key:'count', label:'Count', type:'number', default:4, min:1, max:20 } |
Available from ../utils â these handle common Canvas operations:../utilsããå©çšå¯èœ â Canvasæäœã®å
±éåŠç:
| Function | Description説æ |
|---|---|
dataUrlToCanvas(dataUrl, w, h) | Load a data URL into a Canvas element, scaled to wÃhdata URLãCanvasã«èªã¿èŸŒã¿ãwÃhã«ã¹ã±ãŒãªã³ã° |
loadImage(dataUrl) | Load data URL â HTMLImageElement (for thumbnail gen)data URL â HTMLImageElementïŒãµã ãã€ã«çæçšïŒ |
generateThumbnail(img, size) | Create a thumbnail data URL (default 150px)ãµã ãã€ã«data URLãçæïŒããã©ã«ã150pxïŒ |
A complete, minimal module that manipulates pixel data directly:ãã¯ã»ã«ããŒã¿ãçŽæ¥æäœããæå°éã®ã¢ãžã¥ãŒã«:
// src/lib/efx/modules/sepia.ts import type { EFXModule, Frame, EFXContext, ProcessedFrameData } from '../types'; import { loadImage, generateThumbnail, dataUrlToCanvas } from '../utils'; export const SepiaEFX: EFXModule = { id: 'sepia', name: 'Sepia Tone', icon: 'ð€', category: 'filter', params: [ { key: 'intensity', label: 'Intensity', type: 'range', default: 100, min: 0, max: 100, step: 1 }, ], processFrame: async ( frame: Frame, params: Record<string, any>, context: EFXContext, ): Promise<ProcessedFrameData> => { // 1. Load input into a canvas const canvas = await dataUrlToCanvas( frame.dataUrl, context.canvasWidth, context.canvasHeight ); const ctx = canvas.getContext('2d')!; // 2. Get pixel data const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const d = imageData.data; const mix = params.intensity / 100; // 3. Apply sepia per pixel for (let i = 0; i < d.length; i += 4) { const r = d[i], g = d[i+1], b = d[i+2]; const sr = r * 0.393 + g * 0.769 + b * 0.189; const sg = r * 0.349 + g * 0.686 + b * 0.168; const sb = r * 0.272 + g * 0.534 + b * 0.131; d[i] = Math.min(255, r + (sr - r) * mix); d[i+1] = Math.min(255, g + (sg - g) * mix); d[i+2] = Math.min(255, b + (sb - b) * mix); } ctx.putImageData(imageData, 0, 0); // 4. Return result const dataUrl = canvas.toDataURL('image/png'); const img = await loadImage(dataUrl); const thumbnail = generateThumbnail(img, 150); return { dataUrl, thumbnail }; }, };
Using the makeModule shorthand helper (from color.ts pattern) for simpler Canvas-based effects:color.tsãã¿ãŒã³ã® makeModule ã·ã§ãŒããã³ãã§ãã·ã³ãã«ãªCanvasæç»ãšãã§ã¯ã:
// Even shorter using makeModule (copy this pattern from color.ts) function makeModule(id, name, icon, category, paramDefs, fn): EFXModule { return { id, name, icon, category, params: paramDefs, processFrame: async (frame, params, context) => { const canvas = await dataUrlToCanvas( frame.dataUrl, context.canvasWidth, context.canvasHeight ); const ctx = canvas.getContext('2d')!; fn(ctx, canvas.width, canvas.height, params); // â your logic here const dataUrl = canvas.toDataURL('image/png'); const img = await loadImage(dataUrl); return { dataUrl, thumbnail: generateThumbnail(img, 150) }; }, }; } // Vignette effect using makeModule shorthand export const VignetteEFX = makeModule( 'vignette', 'Vignette', 'ð²', 'filter', [ { key: 'strength', label: 'Strength', type: 'range', default: 50, min: 0, max: 100 }, { key: 'color', label: 'Color', type: 'color', default: '#000000' }, ], (ctx, w, h, p) => { const gradient = ctx.createRadialGradient( w/2, h/2, w * 0.2, // inner circle w/2, h/2, w * 0.7, // outer circle ); gradient.addColorStop(0, 'transparent'); gradient.addColorStop(1, p.color + Math.round(p.strength * 2.55) .toString(16).padStart(2, '0')); ctx.fillStyle = gradient; ctx.fillRect(0, 0, w, h); }, );
// src/lib/efx/registry.ts // Add import at the top: import { SepiaEFX } from './modules/sepia'; import { VignetteEFX } from './modules/vignette'; // Add to EFX_REGISTRY array: export const EFX_REGISTRY: EFXModule[] = [ // ... existing modules ... ColorAdjustEFX, InvertEFX, // ââ Your new modules ââ SepiaEFX, // â just add here! VignetteEFX, // â done! ];
params[] definition.以äžã§å®äº â ä»ã®å€æŽã¯äžèŠã§ããã¢ãžã¥ãŒã«ã¯èªåçã«ä»¥äžã«åæ ãããŸã:params[] å®çŸ©ããèªåçæãããŸãã
flowchart TD
INPUT["frame.dataUrl\nPNG data URL string"] --> CANVAS["dataUrlToCanvas\nscaled to canvasWidth à canvasHeight"]
CANVAS --> PROCESS["Your processing logic\ngetImageData / fillRect / drawImage"]
PROCESS --> EXPORT["canvas.toDataURL\nâ full-size PNG data URL"]
EXPORT --> THUMB["generateThumbnail\nâ 150px thumbnail"]
THUMB --> RETURN["return { dataUrl, thumbnail }"]
my-cool-blur)IDåœå: kebab-caseãã°ããŒãã«ã§ãŠããŒã¯ïŒäŸ: my-cool-blurïŒprocessFrame fast â it may be called on every slider change in Auto modeããã©ãŒãã³ã¹: processFrameã¯é«éã« â Autoã¢ãŒãã§ã¯ã¹ã©ã€ããŒå€æŽã®ãã³ã«åŒã°ããŸãgetImageData + iterate by 4 (RGBA). Skip transparent pixels with if (d[i+3]===0) continue;ãã¯ã»ã«ã«ãŒã: getImageData + 4ãã€ã€ãã¬ãŒãïŒRGBAïŒãéæãã¯ã»ã«ã¯ if (d[i+3]===0) continue; ã§ã¹ãããctx.filter, ctx.globalCompositeOperation, gradients, or any 2D Canvas featureCanvas API: ctx.filterãctx.globalCompositeOperationãã°ã©ããŒã·ã§ã³çãCanvas 2D APIã®å
šæ©èœã䜿ããŸãcolor.ts (pixel manipulation) and distortion.ts (canvas transforms) for real-world examplesæ¢åãã¿ãŒã³: color.tsïŒãã¯ã»ã«æäœïŒãš distortion.tsïŒCanvaså€åœ¢ïŒãåèã«