toybox
toybox · sourcesMulti-layer video compositor. FOUR layers, each rendered into its own framebuffer then reduced to the output by a combine DAG (fade / lumakey / chromakey / map). A layer kind selects its source: GEN = a generative fragment-shader content entry from the bundled bank (noise-fbm domain-warped simplex FBM, worley-cells animated cellular noise; the FX hsv-plasma / cos-gradient palette shaders; the SHADERTOY synthwave-sunset port) with iTime + iResolution + its declared float params on faders — NO scene input. FRAG = a Shadertoy fragment shader that RECEIVES the composited layer below as iChannel0 (recolour / displace / feedback FX, e.g. frag-invert-scan). TOYBOX hosts a faithful SHADERTOY RUNTIME: a `void mainImage(out vec4, in vec2)` source is wrapped through a mainImage→main shim with the FULL Shadertoy uniform set (iTime, iResolution as vec3, iTimeDelta, iFrame, iFrameRate, iMouse vec4 with .z/.w press semantics, iDate, iChannel0-3 + iChannelResolution[4]); the preview canvas routes pointer events to iMouse (client→engine px, GL bottom-origin Y-flip). A GEN/FRAG layer can host a MULTI-BUFFER Shadertoy project (a Common chunk + N buffer passes + an Image pass) — each pass owns its own FBO (RGBA32F via createFloatFbo for intBitsToFloat-packed / signed-precision buffers, degrading to RGBA8), channels resolve to another buffer pass output / its own previous frame (ping-pong feedback) / a keyboard stub / the scene / none, topo-ordered producers-first with Image last; the bundled GROWING PEAK preset is an ORIGINAL multi-buffer project (a growable self-feedback heightmap buffer → a raymarched weather sky Image pass) where CLICKING the preview grows the mountain via iMouse. OBJ = a 3D mesh layer (Phase 3): a bundled CC0 OBJ (Spot the cow, Utah teapot, chess pawn — parsed by an in-house ASCII OBJ parser with auto-center/auto-scale + computed flat normals) OR a built-in procedural primitive (cube / sphere / torus / hypercube tesseract; no asset file), matcap-shaded with depth testing — the matcap is synthesized procedurally in-shader (chrome / clay / neon styles, zero asset-license surface) and the layer carries rotX/rotY/rotZ, scale, spin (auto-rotate) and an RGB tint. An OBJ layer can optionally UV-map ANOTHER layer's rendered output (material.surfaceSource = that layer index, surfaceMix the blend) onto the mesh as a SURFACE TEXTURE in place of (blended with) the matcap — a per-frame dependency-ordered render pass guarantees the source layer renders first, and a self / cyclic / out-of-range source degrades to matcap-only (no WebGL feedback loop). OBJs with no authored texture coords get a planar XY-projected UV so the surface texture isn't collapsed to a single texel. The content/model catalogs + per-shader param schema live in a static manifest (packages/web/static/toybox/manifest.json), enumerated through the dynamic asset-provider seam (bundled + session-registered runtime assets); GLSL + OBJs are fetched lazily on selection (never JS-bundled) and cached. IMAGE = a synced still picture (bytes ride the Y.Doc); VIDEO = a local-file / webcam / patched-feed (inA-inB) / layer-input-tap texture, rounding out the seven layer kinds. RANDOMIZE: the RANDOM button (preset row) rolls a complete curated random patch from a seeded pure engine — it picks among hand-designed structural ARCHETYPE families (weighted, with anti-repeat across consecutive presses), draws content through the asset-provider seam, rolls scalars from designer-tuned ranges (never uniform noise), and INCORPORATES whatever is patched in: a feed on inA/inB becomes a live, load-bearing video layer in every roll and every patched cv1..cv6 port gets a route to a param that exists in the result; rack cables are never created, moved or severed. Rolls RESTRUCTURE the combine graph itself (branch-and-merge diamonds, mid-chain transforms, feedback taps — not just new content in a fixed ladder). LOCKS: a padlock toggle on each layer tab and a Lock item in each op node's right-click menu mark elements IMMUNE from randomize — a locked layer is byte-identical across rolls at its index, a locked node survives with its params, position and upstream feeds (which implicitly locks the source layers it consumes), and existing cv routes onto locked targets are preserved; lock flags ride the locked objects in node.data (synced, saved, exported). Each roll applies atomically in ONE Yjs transaction (one Cmd-Z undoes one roll) and a REVERT button restores the patch as it stood before the session's first roll — honoring locks exactly like a roll does (locked parts keep their current state). Persistence: node.data.layers (4-length array of { kind, contentId, params, material }) + node.data.combine. One video output (out).
the faceplate
inputs
| id | cable | what it does |
|---|---|---|
inA | video | RGB video stream |
inB | video | RGB video stream |
outputs
| id | cable | what it does |
|---|---|---|
out | video | RGB video stream |
source
toybox.ts on GitHub.