903A Random Signal Generator (moogafakkin System 55/35 clone — categorized under Ports -> moogafakkin). Passive white + pink noise source with two independent audio taps (WHITE = flat spectrum; PINK = -3 dB/oct, Voss-McCartney), scaled by a single LEVEL knob. No inputs. Own-code (public-domain noise technique). Beige moogafakkin faceplate (the intrinsic always-on look shared by the moogafakkin module family).
outputs
params
example js
spawn('moog903a', 'x');
set('x', 'level', 0.8);
921 voltage-controlled oscillator (first module of the moogafakkin System 55/35 clone — categorized under Ports -> moogafakkin). Faithful to the original 921: ONE oscillator core presents FOUR simultaneous waveform jacks — sine, triangle, sawtooth, and rectangular with variable pulse width — all phase-coherent off the shared core. Pitch input is exponential 1V/oct (0V = C4); a dedicated LINEAR frequency-control input (lin_fm, scaled by the Lin FM depth) adds +/-Hz rather than octaves — it is one-sided in practice, because the core clamps at a positive 0.01 Hz floor, so a large negative excursion parks the oscillator instead of driving it through zero. A sync input drives the three-position SOFT / OFF / HARD sync switch for classic sync timbres. RANGE sets the coarse octave (+/-5 oct) and FREQ the fine tune (+/-12 st): together those two dials span 12 octaves, 4.09 Hz to 16.74 kHz, and both read 0 at the factory settings where it sings C4. WIDTH sets the rectangular duty cycle (with audio-rate width_cv) and is a pure timbre control — the tap level does not change across its span; LEVEL is the output gain on all four taps. The four jacks are NOT level-matched: at one LEVEL setting they sit within 4.8 dB of each other, rectangular loudest and sawtooth quietest. DSP is own-code: a clean-room polyBLEP/polyBLAMP band-limited oscillator (not a port of any moogafakkin schematic or copyleft source - permissive only). Beige moogafakkin faceplate (the intrinsic always-on look shared by the moogafakkin module family).
outputs
sine audiotriangle audiosawtooth audiorectangular audio
inputs
pitch pitchlin_fm audiosync audiowidth_cv cvoctave cvtune cvlinFmAmount cvlevel cv
params
octave -5..5octtune -12..12stwidth 0.02..0.98linFmAmount -1..1sync -1..1level 0..2
example js
spawn('moog921Vco', 'x');
set('x', 'octave', 0);
patch('x.sine', 'x.pitch');
921B Oscillator (moogafakkin System 55/35 clone — categorized under Ports -> moogafakkin). The slaved VCO driven by a 921A bus: it reads freq_bus (V/oct pitch) + width_bus (pulse width) CONTROL INPUTS from a 921A rather than carrying its own 1V/oct jack, and presents FOUR fixed-level simultaneous waveform outs off one common core — sine, triangle, saw, rectangular — clamped to 0.01 Hz at the bottom and to just under Nyquist at the top (23 520 Hz at a 48 kHz sample rate), not the hardware's nominal 40 kHz. The FREQUENCY pot is a 2-octave fine trim; the RANGE switch sets the octave footage; DC MODULATE is DC-coupled LINEAR FM (non-1V/oct, ±Hz); AC MODULATE is cap-coupled LINEAR FM (a DC-blocking high-pass runs first so a DC offset on the modulator doesn't bend the pitch); the SYNC input + 3-position SYNC switch (off / lo=soft / hi=hard) drive oscillator sync. With nothing patched the bus normals to C4 @ 50% duty so the 921B still sounds standalone, but it is designed to be driven by a 921A driver. DSP forks the shared own-code moogafakkin VCO core (clean-room polyBLEP/polyBLAMP band-limited oscillator + hard/soft sync, the same core the 921 VCO uses) - not a port of any moogafakkin schematic or copyleft source, permissive only. Beige moogafakkin faceplate (the intrinsic always-on look shared by the moogafakkin module family).
outputs
sine audiotriangle audiosaw audiorect audio
inputs
freq_bus cvwidth_bus cvdc_mod audioac_mod audiosync audio
params
fine -12..12strange -5..5octmodAmount -1..1syncMode -1..1level 0..2
example js
spawn('moog921b', 'x');
set('x', 'fine', 0);
patch('x.sine', 'x.freq_bus');
acidwarp is a pure-GPU plasma SOURCE — it has no video input, it synthesizes its picture from math. It is a faithful port of Noah Spurrier's 1992-93 ACIDWARP demo: a 320x240 (NTSC 4:3) buffer of 8-bit palette indices is generated once per scene by a per-pixel formula (distance + angle + scaled sin/cos modulators, plus a few XOR scenes and recursive "rain" noise scenes), then every frame a 256-entry color palette is rotated by one or more slots and sampled per pixel. The scrolling palette is what makes the pattern appear to flow and pulse, even though the underlying index field is static until the scene changes. There are 41 scenes (concentric rings, simple rays, peacock, five-arm star, interference fields, rain/noise, interlaced two-screen variants, etc.) and 8 palettes (RGBW rainbow, greyscale, half-grey, pastel — each with an optional sparkle/"lightning" variant that brightens every 4th slot). Use it as a generative video bed: patch OUT into a mixer/feedback/output card, ride SPEED for the cycling rate, and nudge SCENE by hand or with a clock into scene_cv for rhythmic pattern changes. Slot 0 is reserved black, so palette rotation cycles only the 255 non-black colors.
outputs
inputs
params
speed 0..1freeze 0..1scene 0..?paletteType 0..?sceneTrig 0..1
example js
spawn('acidwarp', 'x');
set('x', 'speed', 0);
patch('x.out', 'x.speed_cv');
One phase accumulator, six taps: saw / square / triangle / sine, a saw→sine→square morph, and a sync pulse. Exponential FM and phase modulation, plus hard sync in.
outputs
saw audiosquare audiotriangle audiosine audiomorph audiosync audio
inputs
pitch pitchfm audiopm audiosync audiotune cvfine cvfmAmount cvpmAmount cvshape cv
params
tune -36..36semifine -100..100centfmAmount -1..1pmAmount -1..1pw 0.05..0.95shape 0..1
example js
spawn('analogVco', 'x');
set('x', 'tune', 0);
patch('x.saw', 'x.pitch');
ARCHIVIST — universal Internet Archive (archive.org) media SOURCE. Pick a media type (IMAGE / AUDIO / VIDEO / ANY), type a search term + press Enter, and the card searches archive.org, picks a RANDOM matching item, and loads it; "↻ next" re-rolls another random match from the same results. Optional FROM/TO year-range narrows the search (e.g. 1970–1989). All searching + metadata happen client-side (the archive.org search + metadata APIs are CORS-open, so NO proxy is needed). PER-TYPE OUTPUTS (subject to archive.org CORS on the SERVED file — verified): IMAGE → the still image is uploaded as a CORS-clean WebGL texture on the `image` output (image upcasts to `video` for free, so it can drive video inputs). AUDIO → the clip plays + scrubs and its stereo audio routes out CORS-clean on `audio_l` / `audio_r` (analysable downstream, e.g. into SYNESTHESIA for beats). VIDEO → archive.org does NOT send CORS headers on served video files, so the clip is PLAY-ONLY: it plays + scrubs in the card preview, but the texture would be tainted, so the `video` output is NOT delivered for an archive video (the card shows a "play-only" warning). SCRUBBING for time-media (audio + video): a draggable timeline seeks the playhead, with ±10s skip, a "jump to random position" button (⤭), and an mm:ss readout — robust to archive.org's byte-range support (verified: audio + video serve HTTP 206 ranges). GATE/TRIGGER + CV OUTPUTS: `loaded` (trigger — a short pulse each time a new item finishes loading, any type), `ended` (trigger — pulses when a time-media item reaches its end), `playing` (gate — HIGH while a time-media item is playing), and `playhead` (CV — 0..1 normalized playhead position). The `play_trigger` gate INPUT toggles play/pause on a rising edge. PATCHING: all inputs + outputs live in the card's yellow drill-down PATCH PANEL (top-left / top-right affordances → INPUT / OUTPUT → grouped Gates / CV / Audio / Video rows) — there are no side jacks. PLAYABLE-FILE PICKING: for video the card prefers an HTML5-playable derivative (h.264 .mp4 → theora .ogv → .webm) chosen by the metadata format token, NOT just the file extension — so an un-decodable MPEG-4-Part-2 / HEVC .mp4 ORIGINAL or an old MPEG-2 / AVI / MOV is skipped; if the picked file still can't decode, the card surfaces a "couldn't play — skipping" status and AUTO-ADVANCES to the next random match (it never hangs on "Loading"). Only publicly-streamable items are loaded (the query excludes access-restricted / lending / DMCA items). Attribution: the card surfaces the item title + a link to its archive.org details page and labels the source as Internet Archive. A clean VIDEO texture output would require a same-origin streaming proxy (re-serving archive.org video with our own CORS + Range passthrough) — out of scope for v1; image + audio ship with real clean outputs, video ships play/scrub-only with the limitation documented.
outputs
image imagevideo videoaudio_l audioaudio_r audioloaded gateended gateplaying gateplayhead cv
inputs
params
gain 0..2cv_play_trigger 0..1
example js
spawn('archivist', 'x');
set('x', 'gain', 0);
patch('x.image', 'x.play_trigger');
System audio input source. Stream from a user-selected mic/line-in/interface via getUserMedia; L+R outputs are fanned out from mono sources or split from stereo. Card owns the permission prompt + device dropdown + devicechange refresh.
outputs
audio_l_out audioaudio_r_out audio
params
example js
spawn('audioIn', 'x');
set('x', 'gain', 1);
Interactive BLOOD video source — the NBlood (Build-engine) port of Blood, compiled to WebAssembly and rendered as a video module. Owner-only and single-instance, like DOOM: the rack owner spawns it and plays; the video output is the Build software-rendered framebuffer, aspect-correct letterboxed into the canvas. CV-typed gate inputs (up/down/left/right, fire, altfire, use, jump, crouch, weapnext/weapprev, esc, enter) drive the marine from cables, and the focused card also captures the keyboard. Stereo audio_l/audio_r outputs are wired but silent in v1 (the PCM bridge is stubbed). The card boots OUT-OF-BOX: the 1997 Blood SHAREWARE data ("The Way of All Flesh", episode 1: maps E1M1-E1M8) is bundled under static/blood/ and LFS-tracked, so no picker is needed. The "Load full Blood data…" picker is an optional override — supply a copy of the full game you own (GOG/Steam One Unit Whole Blood or Fresh Supply) to play all episodes.
outputs
out videoaudio_l audioaudio_r audio
params
audioGain 0..2fillMode 0..1
example js
spawn('blood', 'x');
set('x', 'audioGain', 1);
DTMF dialer with phreaker buttons. 12-key phone keypad — digits 0-9 emit the Bell-System dual-tone pair (row + col, e.g. "5" → 770 Hz + 1336 Hz); BLUEBOX emits a single 2600 Hz sine (the AT&T in-band supervisory tone that 1970s phreakers used to seize long-distance trunks); REDBOX emits 1700 + 2200 Hz summed (the US payphone coin-acceptance pair). Each key is push-to-talk — pointerdown on the card OR a gate cable into the matching gate_<name> input holds the key down. Multiple held keys sum, and shared frequencies (e.g. "1" and "4" both pull col=1209) collapse onto a single shared phase accumulator so simultaneous presses produce a louder tone, not a flam — and a LOUDER one than a pair that shares nothing (1+4 beats 1+5 by 1.76 dB). No musical AD you can dial — bare on/off sines through a one-pole anti-click ramp (τ ≈ 1 ms), inaudibly fast on the way up and about 15 ms to silence on release. The mono sum is scaled by 1/4: one digit sits 18 dB below full scale, eight simultaneous digits reach it, and all twelve keys overshoot by about 2.5 dB.
outputs
inputs
gate_1 gategate_2 gategate_3 gategate_4 gategate_5 gategate_6 gategate_7 gategate_8 gategate_9 gategate_0 gategate_bluebox gategate_redbox gate
params
btn_1 0..1btn_2 0..1btn_3 0..1btn_4 0..1btn_5 0..1btn_6 0..1btn_7 0..1btn_8 0..1btn_9 0..1btn_0 0..1btn_bluebox 0..1btn_redbox 0..1
example js
spawn('bluebox', 'x');
set('x', 'btn_1', 0);
patch('x.out', 'x.gate_1');
Webcam input (LOCAL ONLY). Live <video> -> WebGL2 texture; gain / mirror / on params. The captured stream is local to your browser tab and is NOT sent to other rack-mates — collaborators see a presence badge ("user X has CAMERA active") via Y-awareness, not the video itself. Multiplayer streaming (WebRTC + SFU) is deferred to a future phase.
outputs
inputs
params
gain 0..2enabled 0..1mirror 0..1fillMode 0..1
example js
spawn('cameraInput', 'x');
set('x', 'gain', 0);
patch('x.out', 'x.gain');
Analog-modeled HANDCLAP voice — the fourth member of the drum family (KICK DRUM / SNARE DRUM / TOM DRUM), at deliberately CURATED complexity: one synthesis engine, eight voice knobs + level, spanning the classic clap circuits in one continuous space (TR-808 canonical twin-VCA burst+tail, TR-909 dense bright machine burst, Simmons-ClapTrap spread, LinnDrum-era dark room clap). DSP (own-code, packages/dsp/src/lib/clap-dsp.ts): seeded white noise → COLOR one-pole tilt (log-swept 9 kHz→700 Hz, white→dark, gain-compensated) → Chamberlin band-pass at TONE (400–3000 Hz; the 808 sits near 1 kHz, the 909 near 1.14 kHz) with WIDTH morphing ringy Q≈5.5 → broad splash (1/√q loudness-compensated) → the 808 twin-VCA topology: a BURST of PULSES (2–5) retriggered envelopes SPREAD ms apart (4–25, latched per hit; each pulse decays −60 dB in exactly the spacing — the quad-comparator sawtooth cycles — and the FINAL pulse rings 2× longer, the uninterrupted last discharge) summed with a separate smooth reverb TAIL envelope (30–800 ms, fired at the last pulse, fed one pole darker — the fake room). SNAP is the equal-power burst↔room balance (1 = bone-dry machine clap, 0 = room only); DRIVE a 2×-oversampled warm-tanh; then 20 Hz DC block, dB LEVEL, and a true-peak tanh bound (|out| < 1 always). Inputs: trigger_in (edge-trigger STRIKE — burst geometry + accent latched, noise reseeded: every hit bit-identical), accent_in (per-hit 0..1 CV latched at the strike edge: up to +80 % velocity AND +60 % room excitation — a harder clap is bigger, not just louder), tone_cv (±1.5 oct/V — ±1 V covers the whole knob range), tail_cv (2 oct of tail time per volt), spread_cv (±1.3 oct/V, latched per hit — the ClapTrap random-spread trick, deterministic). Output: ONE mono audio_out. Card: compact BURST·NOISE·ROOM/OUT fader band + a press-to-fire CLAP pad (the `strike` param, OR-ed with trigger_in in the worklet — one clap per press edge).
outputs
inputs
trigger_in gateaccent_in cvtone_cv cvtail_cv cvspread_cv cv
params
pulses 2..5spread 4..25mstone 400..3000Hzwidth 0..1tail 30..800mscolor 0..1snap 0..1drive 0..1level -24..12dBstrike 0..1
example js
spawn('clap', 'x');
set('x', 'pulses', 3);
patch('x.audio_out', 'x.trigger_in');
3D wavetable-navigator oscillator. Builds a 3D scalar field — "the cube," a space that is a mix of solid and filled — out of THREE e352 wavetables (FLOOR / WALL / CEILING, each independently chosen from WAVESCULPT's full preset set, defaults FLOOR=basic-shapes, WALL=pwm-sweep, CEILING=harmonic-sweep — ALL THREE must differ or one control dies by arithmetic: FLOOR=CEILING makes MORPH algebraically inert, and either connector slot matching WALL sends the occupancy function into its degenerate hard-step branch and kills CONNECT and CONNECT STRENGTH outright. The faceplate's table-stack panel draws the three heightfields and names the casualty when it happens), then flies an arbitrary planar SLICE through the field and reads it back as the played waveform via a SURFACE-HEIGHT SCAN: for each of 256 x-positions the sample = how far the solid extends along the slice (intersection depth), so the cube's shape literally becomes the wave. MORPH connects the wall to the floor (at min) or the ceiling (at max), weighted-averaged in between; CONNECT morphs the connecting curve from a circle arc to a sawtooth-V touching the floor, and CONNECT STRENGTH overshoots the connector's interior control point "out of the cube" for a dramatic swell of the solid base (0 = today's exact shape). The slice navigates with Y (up/down) + Rot X/Y/Z (Euler rotation on all 3 axes). MATERIAL switches SMOOTH (continuous density) ↔ HARD (binary solid in/out). CRUSH is a 3D bitcrush reducing spatial-grid + amplitude resolution (default 0 = transparent → blocky/steppy at max); SPACE CRUSH is an independent spatial voxelization of just the FIELD lookup coordinates (chunky voxels, 0 = transparent), and SPACE DIFFUSE applies a gravity that pulls the sampled cloud toward the cube's lowest-information (emptiest) wall — the target wall latches when the tables/morph change, not as the knob moves (0 = off). WRAP toggles whether a slice extending outside the cube is silent (default) or mirror-folds back to the opposite side. A V/oct pitched oscillator (pitch input + tune/fine) with stereo L/R SPREAD on SEPARATE L and R output ports (max ±18% of the cube depth between channels — the L slice is read below center, the R above, and the separate ports mean the offset survives patching into mono inputs; measured, the resulting image is NARROW, about -36 dB of side against mid at the stop with the two channels 0.99998 correlated, so treat SPREAD as a widener rather than a stereo field). View-only Zoom + Rot X/Y orbit the WebGL2 3D cube visualization (dragging the faceplate hero writes the same two angles) (the scalar field rendered as a translucent voxel slice-stack with the live selection slice shown as a square plane cutting through it) without affecting the sound or the selected slice. CV inputs cover the expressive params (slice Y/rotation, morph, connect, connect strength, crush, space crush, space diffuse, fold, tune). A POLY input (polyPitchGate) accepts the 5-voice chord bus from MIDI LANE (mode=poly) or POLYSEQZ: when any lane is gated CUBE runs one phase accumulator per gated lane through the SAME posted slice waves at that lane's pitch and sums them — polyphonic, with the slice/field timbre shared across voices; with nothing patched to poly the mono `pitch` path runs unchanged. A per-voice amplitude ADSR (Attack / Decay / Sustain / Release) plus a BASE VOL knob shape each voice. GATING is decided by what is PATCHED: when the POLY bus OR the mono TRIGGER is connected, CUBE is a GATED voice — a lane/voice sounds only while it is gated-or-releasing, and a never-gated lane is SILENT (patching poly never auto-drones). When NEITHER is patched, CUBE is a continuous raw VCO. BASE VOL is a per-voice VCA FLOOR the envelope rides on top of: gain = base + (1-base)·env per ACTIVE voice — base=0 (the default) is pure ADSR (silent between notes), 0.5 floors at 0.5 and rises to 1.0 at the env peak, base=1 means the env does nothing (full gain while active). It applies ONLY to gated voices: the raw-VCO case (nothing patched) runs at unity independently of BASE, so the legacy continuous drone is byte-identical at every setting. In poly mode each lane's gate edge drives its own envelope (one per voice, soft/click-safe retrigger), and the mix is normalized over ACTIVE voices (1/sqrt(N)) so a sustain=0 held note doesn't pump and a releasing tail doesn't pop. The ADSR + BASE VOL params read live (continuous k-rate) across all stages. Edge detection is block-rate (retrigger granularity floor ≈ one audio block); connectedness (poly/trigger patched) is read from the live patch edges, not bus presence. Pure deterministic field/slice DSP in cube-dsp.ts is reused verbatim by node-ART AND by the card's 3D render, and the surface-height scan runs OFF the audio thread (computed on the main thread, posted to the worklet) so sweeping params never drops the audio out. A VIDEO OUT port carries a live render of that same 3D view to any video module. SYNC is a pure sine off the playback phase that is NOT scaled by LEVEL and runs about 6 dB hotter than the L/R pair, so mixing it alongside them is a level surprise. Only PITCH is audio-rate: every other CV jack is summed into an AudioParam the worklet samples once per block (~375 Hz), quantised to 1/512 of range and 80 Hz-smoothed, so an audio-rate source into one of them aliases rather than modulates.
outputs
L audioR audiosync audiovideo_out mono-video
inputs
pitch cvpoly polyPitchGatetrigger gateslice_y cvslice_rx cvslice_ry cvslice_rz cvmorph_fc cvconnect cvconnect_strength cvcrush cvspace_crush cvspace_diffuse cvfold_cv cvtune cv
params
tune -36..36stfine -100..100¢morph_fc 0..1connect 0..1connect_strength 0..1crush 0..1space_crush 0..1space_diffuse 0..1fold 0..1spread 0..1slice_y 0..1slice_rx -3.1416..3.1416slice_ry -3.1416..3.1416slice_rz -3.1416..3.1416level 0..2attack 0.001..5sdecay 0.001..5ssustain 0..1release 0.001..5sbase_vol 0..1wrap 0..1material 0..1view_zoom 0.3..3view_rot_x -3.1416..3.1416view_rot_y -3.1416..3.1416screen_on 0..1
example js
spawn('cube', 'x');
set('x', 'tune', 0);
patch('x.L', 'x.pitch');
DOOM runs the 1993 shareware game compiled to WebAssembly (doomgeneric) and renders each peer's OWN first-person view to the video 'out' jack, with the WASM SFX mixer bridged to stereo audio outputs. It is a host-only, single-node module (maxInstances 1, ownerOnly): the rack owner adds one DOOM card, clicks the surface to download/cache the ~4 MB shareware WAD and boot the WASM, then either plays solo or hosts a true-lockstep co-op netgame that up to 3 rack-mates one-click hot-join (4 marines total — the owner is player 1) — every peer runs its own runtime and the deterministic tic stream keeps all marines byte-identical. Play with the keyboard once the card is focused (arrows move/turn, Ctrl/F fire, Space uses doors), or drive it from CV: the per-slot gate inputs p1..p4 act as held keypresses (movement/fire/strafe/menu) so an LFO, sequencer, or GAMEPAD can play the marine — each peer applies only its own seated slot's group (own-slot rule), and in single-player only the p1 group is live. Two extra cheat gates inject IDDQD (god mode) and IDKFA (full arsenal) on a rising edge. Game events feed the audio domain as 10 ms gate pulses — per-player weapon fire, door opens, the any-monster kill plus per-monster-type kills, and per-player deaths — so DOOM's action can trigger synths, drums, or a SCOREBOARD. The card's load button, the Single Player / Host Multiplayer start choice, the guest Join button, the click-to-capture-keyboard hint, and the arbiter's New Game dialog (mode/skill/episode/map custom dropdowns + a Launch / Next Map button) are UI controls, not patchable params. There is no host framebuffer mirror — an unjoined spectator simply shows the dark attract screen until it JOINS.
outputs
out videoaudio_l audioaudio_r audioevt_kill gateevt_door gateevt_gun_p1 gateevt_gun_p2 gateevt_gun_p3 gateevt_gun_p4 gate
inputs
params
audioGain 0..2fillMode 0..1cv_iddqd_in 0..1cv_idkfa_in 0..1
example js
spawn('doom', 'x');
set('x', 'audioGain', 1);
patch('x.out', 'x.iddqd_in');
Gate-triggered drum voice (kick / snare / hat morph).
outputs
inputs
gate gatepitch cvtone cvshape cvvolume cvdecay cv
params
pitch -36..36semitone 0..1shape 0..1volume 0..2decay 0.001..0.5s
example js
spawn('drummergirl', 'x');
set('x', 'pitch', 0);
patch('x.audio', 'x.gate');
Pure-TypeScript 6-operator DX7-style FM synthesizer. 32 algorithms, 5-voice polyphony via the polyPitchGate cable, bundled bank of factory-inspired patches (E.PIANO 1, BASS 1, HARMONICA, STRINGS 1, MARIMBA, etc.), and a .syx file picker for loading custom 32-voice cartridge dumps (in-memory only). On top of the six per-operator DX7 envelope generators, a per-voice master OUTPUT-VCA ADSR (Attack / Decay / Sustain / Release) gives a player-dialable amplitude swell / long-release without editing the SYX: one envelope per voice multiplies the summed-carrier output, gated by the same note-on/note-off as the operator EGs (soft/click-safe retrigger). Defaults are ~pass-through (fast attack, full sustain, fast release) so loaded patches sound identical until you touch the master ADSR; a long master release now outlives operator-EG silence (a voice frees only once both the operator EGs and the master amp envelope have faded). NOT a Plaits-backed implementation.
outputs
inputs
poly polyPitchGatepitch_cv cvgate gate
params
algorithm 1..32feedback 0..7voiceCount 1..5level 0..2transpose -24..24stattack 0.001..5sdecay 0.001..5ssustain 0..1release 0.001..5s
example js
spawn('dx7', 'x');
set('x', 'algorithm', 5);
patch('x.out', 'x.poly');
Hybrid audio-visual module that hides a whole signal chain in one box: a miniature SWOLEVCO drives an internal RASTERIZE (audio painted as a drifting raster), which is downsampled to 256×256 and run through a simplified RUTTETRA "XYZ" forward-scatter scope; the XYZ height field is converted in realtime into an animated wavetable (64 frames × 256 samples, throttled to ~24 Hz) fed to an internal WAVECEL wavetable VCO. The on-card 3D wavetable display visibly animates as the field evolves. Exposes WAVECEL's full control + IO surface (tune/fine/morph/spread/fold; pitch/fm + morph_cv/spread_cv/fold_cv; out_l/out_r + scope_out + wave3d_out) plus the mini-SWOLEVCO source controls and the XYZ shape/displacement knobs. The video stages run CPU-side on the main thread (no GL inside the audio node); only the WAVECEL stage is a real worklet.
outputs
out_l audioout_r audioscope_out mono-videowave3d_out videocombined_out video
inputs
pitch pitchfm audiomorph_cv cvspread_cv cvfold_cv cv
params
tune -36..36stfine -100..100¢morph 0..1spread 1..5fold 0..1src_tune -36..36stsrc_fine -100..100¢src_timbre 0..1src_symmetry 0..1src_fold 0..1src2_tune -36..36stsrc2_fine -100..100¢src2_timbre 0..1src2_symmetry 0..1src2_fold 0..1src3_tune -36..36stsrc3_fine -100..100¢src3_timbre 0..1src3_symmetry 0..1src3_fold 0..1xyz_xshape 0..1xyz_yshape 0..1xyz_ydisp -1..1xyz_warp 0..1xyz_zheight 0..1xyz_zoom 1..8xyz_smooth 0..1sync_mode 0..?freezeRasterA 0..1freezeRasterB 0..1freezeRasterC 0..1freezeTable 0..1gen_mode 0..?
example js
spawn('foxy', 'x');
set('x', 'tune', 0);
patch('x.out_l', 'x.pitch');
GIBRIBBON (video) — a Vib-Ribbon spiritual successor rendered with DOOM shareware-WAD sprites. A single white vector "ribbon"/ground line scrolls right→left on black (Vib-Ribbon's exact line-art grammar: the ground dips into a pit V for a LOOP, rises into a hump for a JUMP), while imp (TROO*) and zombie/former-human (POSS*) enemies — REAL sprites decoded from the same DOOM1.WAD the DOOM module uses — ride the ribbon in from the right. The player character is the green DOOM marine (PLAY*). An overhead ABXY prompt strip shows the button each upcoming event needs. FOUR events map to the four ABXY buttons: LOOP (A), JUMP (B), IMP SPAWN (X), ZOMBIE SPAWN (Y); a correct, in-window press clears the obstacle (marine loops/jumps) or fires-and-kills the enemy (it plays its DOOM death animation). Missing an event degrades the marine down a DOOM-flavoured health ladder (super → healthy → wounded → critical → GAME OVER); clean streaks recover rungs and reach a SUPER state. Inputs: cv1..cv4 (modsignal — drive event GENERATION from slow Synesthesia envelopes; each channel maps to one event kind), clock (the 1× scroll/tempo tick), gate (the beat — biases which CV channel spawns), x + y (joystick axes), and four ABXY button gates a / b / x_btn / y_btn (named to disambiguate from the x/y axes). Outputs: out (video), evt_hit / evt_miss / evt_fire / evt_kill / evt_gameover (10 ms gate pulses), and health_cv (marine vitality 0..1). Event generation is a PURE deterministic function of the inputs (gibribbon-events.ts) with all CV→event thresholds in a single tunable GIB_TUNING block; sprite extraction is a PURE WAD picture/PLAYPAL decoder (wad-sprites.ts) run at load time. DOOM1.WAD stays gitignored + fetched like the DOOM module; without it the game falls back to line-art placeholder figures so it still plays. DOOM shareware terms apply (same as the DOOM module).
outputs
out videoevt_hit gateevt_miss gateevt_fire gateevt_kill gateevt_gameover gatehealth_cv cv
inputs
cv1 modsignalcv2 modsignalcv3 modsignalcv4 modsignalclock gategate gatex modsignaly modsignala gateb gatex_btn gatey_btn gate
params
cv1 0..1cv2 0..1cv3 0..1cv4 0..1clock 0..1gate 0..1autoplay 0..1axis_x -1..1axis_y -1..1btn_a 0..1btn_b 0..1btn_x 0..1btn_y 0..1
example js
spawn('gibribbon', 'x');
set('x', 'cv1', 0);
patch('x.out', 'x.cv1');
A procedural source that synthesizes a field of concentric rings centered on the frame, with their phase scrolling inward over time so the bands appear to zoom toward the center (positive Speed) or outward (negative). The shader takes the radial distance from center, multiplies it by Density to set how many rings fit on screen, then subtracts time*Speed to animate the sweep; an abs(sin) wave is soft-banded by Thickness to render alternating bright and dark grayscale rings. There is no video input — it generates its image entirely from time and the three params, so it works without camera permissions. Use it as a hypnotic radial backdrop or a moving mask/wipe source: patch an LFO or envelope into Speed for pulsing zoom, or sweep Density for a tunnel-breathing effect.
outputs
inputs
speed cvdensity cvthickness cv
params
speed -2..2density 1..50thickness 0..1
example js
spawn('inwards', 'x');
set('x', 'speed', 0);
patch('x.out', 'x.speed');
Extended Karplus-Strong STRING/HARP VOICE built on the COFEFVE DELAY's own fractional delay-line core (owner directive — no parallel delay implementation): a strike fires a seeded noise burst into a recirculating delay-line "string" and the ring IS the note, decaying like a real vibrating string. The full Jaffe–Smith/CCRMA EKS extension set, curated to 6 voice knobs + CV: DECAY is t60 in real SECONDS, frequency-compensated with the ρ = 0.001^(1/(f0·t60)) per-period loss law so low notes do NOT ring 10× longer than high ones (the classic K-S sin, fixed); BRIGHT is the string material — an in-loop damping low-pass whose cutoff TRACKS the note (fc = f0·2^(0.5+5.5·knob), so ≈1.4×f0 felt-dark … 64×f0 steel-bright, the Rings damping vocabulary), phase-compensated at f0 so it never detunes the note (below BRIGHT ≈0.1 the safety cap on loop gain does shorten it — a dark string cannot ring for 10 s); POS is the pick position (feedforward comb at β of the period — β=0.5 cancels even harmonics for a hollow mid-pluck, small β = bridge-thin); STIFF adds dispersion allpasses that stretch upper partials SHARP toward piano wire / bell / gong; COLOR (200 Hz mallet → 10 kHz pick low-pass) and BURST (0.1–4 PERIODS of noise, energy-normalized: near-impulse tick → classic 1-period pluck → scraped/bowed onset) shape the exciter. Tuning: 1 V/oct (f0 = Tune × 2^V, 30 Hz–4.2 kHz) with every loop stage's phase delay at f0 compensated in closed form and the fraction realized by the cofefve Catmull-Rom read — measured ≤ 0.1 cents across C2–C7 (unit-gated < 3). Inputs: trigger_in (edge-trigger STRIKE, burst reseeded per hit — deterministic), pitch (1 V/oct), accent_in (per-hit velocity latched at the edge: louder + brighter), damp_in (level-sensitive palm mute WHILE high, ~50 ms choke), plus a per-control CV on EVERY one of the eight knobs (tune_cv/decay_cv/bright_cv/position_cv/stiff_cv/color_cv/burst_cv/level_cv — each ±1 sweeps its param full-range around the knob, all 80 Hz-smoothed in the worklet, so `pitch` is the module's only genuinely audio-rate input). Mono out (the string tap, taken pre-damping) with LEVEL −24..+12 dB. Card: STRING·EXCITER fader bands + a PLUCK audition button (fires the canonical shared trigger pulse). RACKLINE face (?shell=1): hero ranks DECAY + BRIGHT with a live output scope; pages string·ring / pick·strike / output; rear card groups the four play jacks into a striking hand (STRIKE + ACCENT) and a fretting hand (1 V/OCT + DAMP). Stability is proven, not hoped: modes k ≥ 1 are bounded by ρ < 1 and the k = 0 loop mode is pinned below unity by an f0-tracked DC blocker (unit-gated at the hostile knob extremes).
outputs
inputs
trigger_in gatepitch pitchaccent_in cvdamp_in gatedecay_cv cvbright_cv cvposition_cv cvstiff_cv cvcolor_cv cvtune_cv cvburst_cv cvlevel_cv cv
params
tune 55..1760Hzdecay 0.1..10sbrightness 0..1position 0.02..0.5stiffness 0..1color 0..1burst 0.1..4level -24..12dB
example js
spawn('karplus', 'x');
set('x', 'tune', 220);
patch('x.out', 'x.trigger_in');
Layered stereo kick VOICE — the "shake the house" deep-bass kick (own-code). Three DECOUPLED generator layers so depth and punch live on orthogonal knobs: a pure-sine SUB (Tune 20–120 Hz, gentle slow settle, long Sub Dec ≤ 800 ms — the air-moving pulse), a band-limited BODY one octave up (fast downward P Amt/P Time pitch sweep — the 909 "dooo" chest-thump — plus Shape sine→tri→rect morph and a Tension amplitude→pitch glide), and a filtered-noise CLICK transient (Click len / Clk Tone / Clk Lvl). The summed layers run a serial bus: DRIVE saturation with a single HARD character switch (clean-warm vs aggressive, owner-decided instead of a mode menu), an own-code 3-band kick EQ (Sub/Body/Atk EQ bells + a spectral Tilt), and the TRANSLATE harmonic exciter that synthesizes the sub's 2nd/3rd/4th harmonics so a 40–50 Hz fundamental still reads deep on laptop/phone speakers. DYNAMICS: a threshold-free transient shaper (Attack/Sustain), a GLUE compressor whose detector is sidechain-HPF'd ~100 Hz so the sub never pumps it, and a CEILING soft-clip that true-peak-bounds the voice so it can sit hot. Stereo stage: everything <120 Hz is strictly MONO (phase-safe, full excursion); Width spreads only the upper body/click band (M/S) — separate audio_l/audio_r outs with stereoPairs auto-pairing. Inputs: trigger_in (edge-trigger STRIKE — phases reset, envelopes fire), accent_in (per-hit 0..1 CV latched at the strike edge, scaling sweep depth + level), pitch_cv (1V/oct, transposes the whole voice), choke_in (level-sensitive gate — damps while high through a short ramp, releases on the falling edge), PLUS a per-control CV input for every one of the 25 voice knobs (tune_cv … level_cv — each ±1 sweeps its param full-range around the live knob, cvScale matched to the knob curve). Level spans −24..+12 dB with deliberate headroom, guarded by the internal ceiling. Wide 3u banded card: SUB·BODY·CLICK / DRIVE·EQ·TRANSLATE / DYNAMICS·STEREO·OUT. Ships as a clean-deep club kick by default; later DSP phases land inside the worklet without changing this contract (Phase 1 renders SUB+BODY, L=R).
outputs
audio_l audioaudio_r audio
inputs
trigger_in gateaccent_in cvpitch_cv cvchoke_in gatetune_cv cvsub_decay_cv cvsub_level_cv cvpitch_amt_cv cvpitch_time_cv cvtension_cv cvbody_decay_cv cvbody_level_cv cvbody_shape_cv cvclick_len_cv cvclick_tone_cv cvclick_level_cv cvdrive_cv cvhard_cv cvtranslate_cv cvsub_eq_cv cvbody_eq_cv cvattack_eq_cv cvtilt_cv cvattack_cv cvsustain_cv cvglue_cv cvceiling_cv cvwidth_cv cvlevel_cv cv
params
tune 20..120Hzpitch_amt 0..48stpitch_time 5..120mstension 0..0.6sub_decay 50..800msbody_decay 20..400msclick_len 2..60mssub_level 0..1body_level 0..1click_level 0..1body_shape 0..1click_tone 500..6000Hzdrive 0..1hard 0..1translate 0..1sub_eq -12..12dBbody_eq -12..12dBattack_eq -12..12dBtilt -1..1attack -1..1sustain -1..1glue 0..1ceiling 0..1width 0..1level -24..12dB
example js
spawn('kickdrum', 'x');
set('x', 'tune', 50);
patch('x.audio_l', 'x.trigger_in');
LINES is a procedural mono-video source that renders soft-edged parallel stripes whose orientation, count, thickness, and scroll you dial in. The shader rotates the UV space by Orient (0 = horizontal lines, 1 = vertical, anything between = diagonal), then computes wave = abs(sin(2pi * Amp * (position + Phase))) along that axis and lights up bright bands wherever the wave falls under the Thickness threshold, with a smoothstep soft edge straddling it. The result is a grayscale grating written equally to all three RGB channels (alpha 1). The pattern auto-scrolls on its own (Phase advances steadily over time, time * 0.15 wrapped to 0..1) so it is visibly alive without touching a knob; your Phase value adds on top of that drift. Patch the OUT into an OUTPUT screen, a video mixer, or a colorizer; use it as a structural test pattern or as a moving modulation texture for downstream video modules.
outputs
inputs
fm mono-videoorient cvamp cvthickness cvphase cv
params
orient 0..1amp 0.5..50lpxthickness 0..1phase 0..1fmDepth 0..1
example js
spawn('lines', 'x');
set('x', 'orient', 0);
patch('x.out', 'x.fm');
The BROWSER VIEWPORT as a video source (LOCAL ONLY). Zero inputs, one video OUT whose contents are what you currently SEE in this tab — the visible canvas pane — so you can feed LOOPBACK -> RECORDERBOX to record your viewport, or -> any effect for live self-referential feedback. Mechanism: the current tab is captured via the Screen Capture API (getDisplayMedia({ video: { displaySurface: "browser" }, preferCurrentTab: true, selfBrowserSurface: "include" })), run in a hidden <video>, and the engine samples each frame into a WebGL2 texture exactly like CAMERA; a crop step then windows the tab frame down to the app viewport element's on-screen rectangle (re-measured every frame by a node-scoped pump that outlives any one card mount, and pushed to the engine as LOCAL per-viewer state — never synced) so OUT is just the active viewport, letterbox-fit into the engine frame (black bars, never cropping edges away). Because the preview shows the tab it is captured from, the live preview is intentionally recursive (a video-feedback tunnel). Params: gain (0..2 RGB multiplier), crop (viewport vs whole-tab, ON by default). STARTING A CAPTURE ALWAYS TAKES A CLICK — getDisplayMedia has no already-granted state, so unlike CAMERA nothing can resume a capture automatically: every capture needs a fresh user gesture and a fresh trip through the picker. That gesture is START CAPTURE on the faceplate under the default shell, or the card's Start capture button under ?shell=legacy. A capture ends with STOP or from the browser's own share bar (the module returns to idle with a re-capture button); getDisplayMedia is feature-detected, degrading to a disabled unsupported state with the reason where the API is missing.
outputs
params
example js
spawn('loopback', 'x');
set('x', 'gain', 0);
LUSH GARDEN — generative layered-garden video SOURCE. Assembles a dense 2D English-garden bed from a bank of plant cutouts (flowers/bushes/small trees, ~70/20/10 spawn mix) on a ground plane running from the bottom edge up to an INVISIBLE horizon (the HORIZON knob moves placement geometry only — no line is ever drawn at it). Plants spawn continuously at RATE spawns/sec, grow in from their ground anchor (~350 ms ease-out), scale down with depth (perspective) on top of the tree > bush > flower kind scale, and composite back-to-front; VIEW pans a viewport across a ~2.5-frame-wide world with depth-proportional 2D parallax (near sweeps, far crawls). At the 350-plant cap each spawn replaces the OLDEST plant so the bed keeps evolving. Patch a gate into GROW and continuous spawning stops — exactly ONE plant grows per rising edge ([GATED] badge); a RESET edge clears the bed. FOUR simultaneous outputs render the same scene: CLEAN (plain composite, the card preview), MONO (white silhouette outlines via an alpha-Sobel baked per cutout), WATERCOLOR (colours bled strictly inside each silhouette), PSYCHEDELIC (animated hue-cycled colours, per-plant phase). An optional BACKGROUND video passes through unprocessed behind the plants on all four outputs. Cutouts load lazily from static/lushgarden (manifest-driven; outline+watercolor baked once per cutout at load so the 4 outputs stay cheap; unconnected outputs are skipped). All ports live on the yellow drill-down PATCH PANEL (no raw side jacks, #767).
outputs
mono videowatercolor videopsychedelic videoclean video
inputs
background videorate cvhorizon cvview cv
params
rate ?..?horizon 0..1view 0..1fov 0..1freeze 0..1
example js
spawn('lushgarden', 'x');
set('x', 'rate', 0);
patch('x.mono', 'x.background');
Plaits-style macro oscillator (Mutable Instruments archetype). Clean-room pure-TypeScript implementation — not a port of Plaits' C++ source (see PR #27 for the closed emscripten attempt). First-slice scope ships two synthesis models behind the three canonical macros (HARMONICS / TIMBRE / MORPH): (0) virtual analog (VA) — morphing saw→square→triangle PolyBLEP wave + detuned partner (HARMONICS = detune amount) + wavefolder (TIMBRE = fold amount); (1) waveshape — sine through a morphable wavefolder/tanh-waveshaper (TIMBRE = drive, MORPH = wavefolder↔tanh, HARMONICS = sub-octave mix). PITCH input is V/oct; NOTE param is a ±60-semitone offset on top. TRIG resets phase on rising edge for percussive attack alignment. OUT is the level-scaled main output; AUX is a per-model raw tap (unfolded sub-octave triangle in VA, pre-distortion body in waveshape). More models (granular, FM, chord, speech, kick/snare/hat, modal, etc.) land in follow-up PRs.
outputs
inputs
pitch pitchtrig gatemodel_cv cvnote_cv cvharm_cv cvtimb_cv cvmorph_cv cvlevel_cv cv
params
model 0..?note -60..60stharmonics 0..1timbre 0..1morph 0..1level 0..1
example js
spawn('macrooscillator', 'x');
set('x', 'model', 0);
patch('x.out', 'x.pitch');
A WebGL2 ray-marched 3D Mandelbulb fractal source that doubles as an audio oscillator. A single full-screen-quad fragment shader marches the power-8 Mandelbulb distance estimate, shades the hit surface with finite-difference normals, diffuse + Phong specular and a soft shadow, tints it with the Hue palette, and emits the render on video_out (4:3, ray-marched internally at half engine resolution — 512x384 at the 1024x768 default — and LINEAR-upscaled). An orbit camera (Zoom dolly + Rot X pitch / Rot Y yaw) frames the bulb; Power morphs the fractal shape and Detail sets the iteration budget (higher = crisper, costlier). Turn SLICE on to bridge into audio: a fixed-size plane (camera-independent) is marched through the bulb's distance field to read its cross-section as a 256-sample wavetable, played as an oscillator on audio_out and shown as a second on-card readout with a draggable yellow select box. Usage: patch a slow LFO into rotate_y_cv (or just leave SPIN on) for a tumbling fractal, modulate power_cv for shape-morphing, and enable SLICE to play the bulb's geometry as an evolving waveform.
outputs
video_out mono-videoaudio_out audio
inputs
zoom_cv cvrotate_x_cv cvrotate_y_cv cvpower_cv cvdetail_cv cvhue_cv cvslice_y_cv cvslice_rx_cv cvslice_ry_cv cvslice_rz_cv cv
params
zoom 0.3..3rotate_x ?..?rotate_y ?..?power 1..12detail 4..30hue 0..1autospin 0..1screen_on 0..1slice 0..1slice_y ?..?slice_rx ?..?slice_ry ?..?slice_rz ?..?freeze 0..1
example js
spawn('mandelbulb', 'x');
set('x', 'zoom', 0);
patch('x.video_out', 'x.zoom_cv');
Random sampler / clock generator (Mutable Instruments Marbles archetype, Émilie Gillet, MIT-licensed). Clean-room TypeScript port of the eurorack/marbles/ DSP. The T-section (t1 / t2 gates) generates clocked random gates via one of six models — COIN (complementary Bernoulli), CLUSTERS, DRUMS (18 built-in 8-step patterns), INDEP (independent Bernoulli), 3-STATE, MARKOV — with a déjà-vu loop that locks the random stream into a repeating pattern (RATE / T BIAS / T JITTER / DÉJÀ VU / LENGTH). The X-section (x1 / x2 / x3 CV) draws random voltages shaped by SPREAD (variance), X BIAS (mean), and STEPS (quantization amount + STEPS-knob portamento), snapped through a weight-aware variable-resolution quantizer onto one of six scales (C major / C minor / Pentatonic / Pelog / Raag Bhairav / Raag Shri), with its own déjà-vu loop shared across the three X channels via pseudo-random hash shifts. clk is the master clock. CV outs are ±1 (= ±5V). Beta-distribution sampling is approximated analytically vs the firmware's precomputed table; the déjà-vu / Markov / quantizer / lag logic is ported line-for-line.
outputs
t1 gatet2 gatex1 cvx2 cvx3 cvclk gate
inputs
rate_cv cvtmodel_cv cvtbias_cv cvtjitter_cv cvdejavu_cv cvlength_cv cvspread_cv cvxbias_cv cvsteps_cv cvxdejavu_cv cvscale_cv cv
params
rate -60..60stt_model 0..?t_bias 0..1t_jitter 0..1deja_vu 0..1length 1..16pw_mean 0..1spread 0..1x_bias 0..1steps 0..1x_deja_vu 0..1x_length 1..16scale 0..?
example js
spawn('marbles', 'x');
set('x', 'rate', 0);
patch('x.t1', 'x.rate_cv');
Gate-triggered cat-vocal synth voice (formant bank + harmonic + noise excitation).
outputs
inputs
gate gatepitch pitchmorph cvdecay cvlevel cv
params
pitch -36..36semimorph 0..1decay 0.05..2slevel 0..2
example js
spawn('meowbox', 'x');
set('x', 'pitch', 0);
patch('x.L', 'x.gate');
MIDI LANE — a per-channel "instrument bus" demux for a hardware MIDI sequencer (Reliq, Cre8audio Programm, Empress ZOIA, or any class-compliant USB-MIDI device). DAW-style workflow: assign each track of your sequencer to its own MIDI channel, then drop one MIDI LANE per instrument and point each at that track's channel — multi-timbral = several lanes, like several MIDI-CV-BUDDYs but channel-aware and richer. Each lane demuxes its channel into the CV/gate the rack speaks: pitch_cv (V/oct, 0V = C4 = MIDI 60, pitch-bend summed at ±2 st), gate (HIGH while any key on the lane is held; with RETRIG, dips one audio block on each new note-on so a downstream ADSR re-fires), velocity_cv (0..1), plus TWO learn-assignable CC taps (cc_a / cc_b → 0..1 CV — hit LEARN, wiggle a CC, done; these subsume the per-track CC-modulation lane and can drive audio params OR video params via the cross-domain bridge), plus ONE by-note-number drum gate (note_gate fires on a card-selected MIDI note, default GM kick = 36 — the Programm/Reliq ch10 drum-router pattern generalized via configuration, not 8 fixed ports). MONO mode is monophonic with three voice-priority modes (LAST / LOW / HIGH); POLY mode adds a 10-channel polyPitchGate output (poly) so a chord on the lane plays a polyphonic synth (cartesian / dx7). The SAME outputs drive VIDEO modules for free: a gate or cv handle cabled into ACIDWARP.scene_cv / DOOM.cv_pN fires visuals with no synth voice — the engine's cross-domain CV/gate→video bridge needs no special "video gate" port. Uses the browser's built-in Web MIDI API (no third-party library, no native bridge); click "Connect MIDI…" once per origin to grant access. Main-thread plumbing (ConstantSource-per-output, 2 ms scheduling lookahead) identical to MIDI-CV-BUDDY, whose note logic it reuses verbatim. v1 surfaces a single-channel-or-ALL selector on the card (the engine supports a multi-channel Set under the hood for a future multi-select). End-to-end Web MIDI latency is the honest ~5-10 ms main-thread path.
outputs
pitch_cv cvgate gatevelocity_cv cvcc_a cvcc_b cvnote_gate gatepoly polyPitchGate
example js
spawn('midiLane', 'x');
Hardware MIDI controller → pitch + gate + velocity CV. Uses the browser's built-in Web MIDI API (no third-party library) and converts incoming note-on / note-off / pitch-bend messages into three ConstantSourceNode outputs: pitch_cv (V/oct, 0V = C4 = MIDI 60, with pitch-bend summed in at the MIDI-standard ±2 semitones), gate (0/1), and velocity_cv (0..1). Monophonic with three voice-priority modes (LAST = newest key wins, the conventional default; LOW = lowest key, classic mono-bass behavior; HIGH = highest), a RETRIG toggle that drops the gate to 0 for one audio block between successive note-ons (so a downstream ADSR re-fires) versus legato (gate stays high through key changes), an ALL/1..16 channel filter, and a device-picker dropdown that hot-plugs when controllers connect/disconnect. The user clicks "Connect MIDI…" once per origin to grant permission; subsequent reloads reuse the grant. End-to-end latency is honest about the Web MIDI main-thread path (~5-10 ms typical on Chrome/macOS); event.timeStamp is mapped to ctx.currentTime + a 2 ms lookahead so scheduling lands at the start of the next audio block rather than mid-block.
outputs
pitch_cv cvgate gatevelocity_cv cv
example js
spawn('midiCvBuddy', 'x');
Hardware MIDI transport bridge. Locks to a MIDI device and surfaces the System Real-Time stream as gate/CV: clock (gate) at a user-selectable subdivision — 24=quarter (default, patch directly into TIMELORDE.clock to slave it to the external transport), 12=eighth, 6=sixteenth, 3=32nd, 1=raw 24 PPQN; run (cv, 0/1) tracks transport state; midistart + midistop fire one-shot gates on MIDI Start (0xFA) and Stop (0xFC). Continue (0xFB) raises run without re-firing midistart, so downstream loops resume in place. Channel-voice messages are ignored — pair with MIDI-CV-BUDDY for note/velocity. Same Web MIDI / ConstantSource / 2 ms lookahead plumbing as MIDI-CV-BUDDY.
outputs
clock gaterun cvmidistart gatemidistop gate
example js
spawn('midiclock', 'x');
MILKDROP — a Winamp/Milkdrop music visualizer (wrapping the open-source butterchurn WebGL2 engine + ~20 curated classic presets) as a fully CV-instrumented video SOURCE. Patch audio into AUDIO and the visuals react (the tap is inaudible). The novel part: butterchurn drives nearly all preset motion from three audio scalars — bass/mid/treb — and MILKDROP lets a cable REPLACE any of them. Patch CV into BASS/MID/TREB to drive that band from the cable instead of the live audio (an unpatched band still follows the audio); REACT scales all three. SPEED time-warps the engine clock (clamped at 0), PRESET selects the active preset (quantized knob/CV), MORPH sets the crossfade seconds, and a rising edge on NEXT advances presets hands-free. OUT is a normal downstream video texture (route into a mixer / keyer / OUTPUT). The card has a live preview + preset name/index readout + RCT/SPD/PST/MPH knobs, and hide-controls turns it into a resizable monitor. The butterchurn engine lives in node_modules (not vendored into the WebGL attest basis) and the preset pack loads behind a dynamic import() as a separate chunk. All ports live on the yellow drill-down PATCH PANEL (no raw side jacks, #767).
outputs
inputs
audio audiobass cvmid cvtreb cvreactivity cvspeed cvpresetSelect cvmorph cvnext gate
params
bass 0..2mid 0..2treb 0..2reactivity 0..2speed 0..2presetSelect 0..?morph 0..8nextTrig 0..1
example js
spawn('milkdrop', 'x');
set('x', 'bass', 0);
patch('x.out', 'x.audio');
MIRRORPOOL — a WebGL2 VIDEO source rendering a hemisphere pool of liquid inside a box, viewed by a full-PTZ camera you fly around within it. TWO video inputs feed the optics: POOL is surface-mapped to the INSIDE of the hemisphere (the underwater view you see refracted through the water, with Beer-Lambert depth absorption + chromatic dispersion) and SCENE is the surroundings, reflected off the surface as an overhead backdrop (projected to the box ceiling so a camera pan slides a RECOGNISABLE reflection, not a lat-long smear). A single REAL height field drives all the optics — not a flat normal-map fake: WIND raises a set of 4 directional swell waves whose amplitude grows with wavelength (dispersion omega=sqrt(g*k), so bigger crests are genuinely TALLER and travel FASTER), and RAIN runs a seeded Poisson scheduler spawning raindrop impacts that punch one-shot dimples which expand into propagating rings — sparse shallow drizzle up to a dense deep downpour. The surface NORMAL reconstructed from that height field (by finite differences, one consistent sign convention shared by the sim + swell so they never cancel) drives a physically-based Schlick FRESNEL split (water F0=0.02, eta=1/1.33). In the default REFRACT mode (MODE=0) you see the reflected scene layered over the refracted, caustic-lit pool beneath; sweep MODE toward MIRROR (1) and the surface becomes a near-full mirror of the sky/scene that the ripple normals shatter and distort — one continuous CV-friendly scalar, no branch. BRIGHT is a virtual sun scaling overall scene light uniformly (no sun disc drawn yet). The camera is a repositionable ORBIT + FREE-LOOK rig surfaced as the card's TWO X-Y pads: pad 1 POSITIONS the eye on a sphere around the pool (Orbit = azimuth around it, Elev = elevation from straight overhead down BELOW the water surface, plus a Dist dial), and pad 2 is the free-LOOK offset (Look X = yaw, Look Y = pitch) off the default aim-at-centre so the view can point any direction; Zoom sets a 70..20 degree FOV. Dropping the elevation below the surface flips the render into an underwater Snell-window path (a bright refracted cone of the sky/scene overhead, ringed by total-internal-reflection of the pool interior). Every control has a matching CV input (port id == param id + `_cv`), so patch an LFO into orbit_az_cv for a slow orbit, noise into rain_cv to gust the storm, or an envelope into surface_mode_cv to melt between a clear pool and a hard mirror. The rain wave sim runs as a float ping-pong wave-equation FBO when the GPU exposes renderable float targets, and falls back to an analytic expanding-ring model in the render shader when it does not (isFloat=false, e.g. some SwiftShader configs) so rain is never a dead field. Rendered at half engine resolution and LINEAR-upscaled (the mandelbulb perf discipline). With nothing patched it still renders a live procedural sky + rippling water, so it doubles as a standalone generative source. All ports live on the yellow drill-down PATCH PANEL (no raw side jacks). The pure physics (Fresnel / swell gradients / normal reconstruction / Poisson rain / orbit + free-look camera) is unit-tested in $lib/video/mirrorpool-core (the exact CPU mirror of the shader).
outputs
inputs
pool videoscene videowind_speed_cv cvwind_dir_cv cvrain_cv cvbrightness_cv cvsurface_mode_cv cvorbit_az_cv cvorbit_el_cv cvorbit_dist_cv cvlook_yaw_cv cvlook_pitch_cv cvzoom_cv cv
params
wind_speed 0..1wind_dir ?..?rain 0..1brightness 0..2surface_mode 0..1orbit_az ?..?orbit_el -1.45..1.45orbit_dist 0.4..5look_yaw ?..?look_pitch -1.45..1.45zoom 0..1freeze 0..1
example js
spawn('mirrorpool', 'x');
set('x', 'wind_speed', 0);
patch('x.video_out', 'x.pool');
A playable QBasic-Nibbles snake game rendered as a patchable video source. The classic snake roams an 80x50 grid (CPU-rasterised to a 320x200 frame with a gentle CRT scanline darken) eating one pellet at a time, growing its body, and dying on a wall or self collision. Drive it two ways: click the card to focus it and steer with the arrow keys, or flip AUTO on to let the built-in greedy bot self-play (it walks toward the pellet, avoiding its own tail, with no foresight so it eventually traps and dies — then auto-restarts). The game advances at the rate set by Tick. Beyond the video frame, the snake's life becomes control voltage and sound: gate pulses fire on pellet/death/direction-change, a length CV tracks how long the snake has grown, and two square-wave audio outs are pitched by the snake's length (length 4 = A2/110 Hz, every +12 length = +1 octave). Patch the gates into envelopes/triggers and the length CV into pitch or filter cutoff to sonify the game. The card also has a RESET button, a 1x-4x zoom button, and a live LEN readout (a dagger appears when the snake is dead). The card's game screen is resizable: the on-card scale button cycles the 320x200 source through 1x / 2x / 3x / 4x zoom (image-rendering: pixelated, so it stays crisp); the knobs, buttons, and patch jacks stay fixed-size while only the screen grows.
outputs
out videopellet gatedeath gatedir_change gatelength_cv cvsnake audiogated audio
params
example js
spawn('nibbles', 'x');
set('x', 'auto', 0);
Basic noise source. Three independent audio outputs — WHITE (full-spectrum), PINK (1/f, -3 dB/oct via Voss-McCartney), BROWN (leaky-integrated white: flat below a ~77 Hz corner at 48 kHz, -6 dB/oct above it). All outputs share a single LEVEL knob, which is one multiplier and not one loudness — brown leaves 7.1 dB and pink 12.3 dB below white. No CV inputs.
outputs
white audiopink audiobrown audio
params
example js
spawn('noise', 'x');
set('x', 'level', 0.5);
Numpad-driven 4-layer × 16-step sequencer + live keyboard. Each numpad note key fires the active layer's pitch+gate immediately AND, when REC ARM (one-pass record on next play-from-start) or OVERDUB (always-recording) is on, writes the note to the nearest step on the active layer. Default keymap: 1=C, 2=C#, 3=D, 4=D#, 5=E, 6=F, 7=F#, 8=G, 9=G#, 0=A, /=A#, *=B, with Numpad+ / Numpad- bound to OCTAVE UP / OCTAVE DOWN. All fourteen bindings are REMAPPABLE to any physical key — left-click a key cap on the faceplate (or right-click for the Remap / Reset menu) and press the key you want — and the map is saved with the patch, per node. Octave 0-8 (named c0..c8 by the note the first key plays) also moves from the faceplate. CV inputs: clock (rising-edge external clock — internal BPM ignored while patched) + layer (unipolar 0..1 CV selecting the active layer, otherwise the activeLayer param wins). CV outputs: l1_pitch / l1_gate ... l4_pitch / l4_gate so a patch can route each layer to its own downstream synth — basically a 4-track sequencer — plus a POLY bus carrying the active layer's voices for a poly-aware voice, NINE outputs in total. When this module exists in the rack its keyboard listener captures whatever event.codes its keymap binds and preventDefaults them, so other modules never see those keys; text fields and a focused DOOM card are excluded. The listener lives in the module itself rather than on any card, so it works with no UI open.
outputs
l1_pitch pitchl1_gate gatel2_pitch pitchl2_gate gatel3_pitch pitchl3_gate gatel4_pitch pitchl4_gate gatepoly polyPitchGate
inputs
params
bpm 30..300isPlaying 0..1activeLayer 0..3recArm 0..1overdub 0..1octave ?..?poly 0..1
example js
spawn('numpadPlus', 'x');
set('x', 'bpm', 120);
patch('x.l1_pitch', 'x.clock');
OUTLINES — stateful particle video generator (LZX-style primitive source; formerly CIRCLES, renamed when the SHAPE selector landed). A GATE event (or the internal RATE clock) spawns a SHAPE at a SEEDED-random position in a 1024-px field; each shape latches its diameter / vector / speed / decay / SHAPE at spawn and moves in that direction, BOUNCING when its CENTER-POINT hits a wall (the velocity reflects; no edge/radius collision math for the WALL, so a fat shape may briefly overhang). SHAPE picks one of six forms — circle, triangle, square, pentagon, hexagon, octagon — each polygon a REGULAR N-gon inscribed in the diameter (every vertex on the circle of radius d/2 — the circumradius), so all six share one bounding-circle radius (d/2) and the COLLIDE math is unchanged across shapes. ROTATION is a LIVE GLOBAL bipolar spin: knob CENTER = no rotation, left extreme = fast CCW, right extreme = fast CW; every live shape shares one rotation angle that accumulates by that angular velocity each frame, so the whole field spins coherently and the spin shows up consistently in the rendered geometry AND in every output (the rotated polygon vertices drive the overlap-count the outputs read; a circle is rotation-invariant so only the 5 polygons visibly turn). With the COLLIDE gate HIGH, shapes ALSO bounce off EACH OTHER: a bounding-circle elastic collision — two shapes collide when the distance between their CENTERS ≤ (r1 + r2), i.e. their circumcircles touch (unlike the center-based wall bounce, this uses the radii), and an equal-mass ELASTIC response swaps the velocity components along the center-to-center normal and separates the pair so they don't stick (each keeps its independent latched SPEED as far as elastic physics allows). Gate LOW / unpatched → shapes PASS THROUGH each other (the default). COLLIDE is a LIVE GLOBAL mode (read every frame), NOT spawn-latched. The inter-shape pass is O(n²) over the active list, bounded by the 200-shape cap (~10k pair tests/frame). Every per-shape property is LATCHED at spawn from the live knob+CV — crucially SPEED and SHAPE: a shape integrates from its OWN latched velocity (and keeps its own latched form) for its whole life, so turning SPD or SHAPE after a shape exists affects ONLY newly-spawned shapes, never the ones already flying. Model: a JS list of active shapes {x,y,vx,vy,diameter,decayS,ageS,alpha,shape,sides,baseAngle} integrated on the engine rAF. Controls — D: DIAMETER (5..270 px, the circumdiameter); V: spawn VECTOR ANGLE (full range = 0..360°, every angle reachable); SPD: SPEED (0 = static scatter, up to 300 px/s ≈ crosses the field in ~3 s; latched independently per shape); DECAY: per-shape FADE-OUT time — 0 = NO decay (the shape PERSISTS, the static-field case, FIFO-capped) ramping up to a 10 s fade where the shape's alpha ramps 1 → 0 and it is removed; SHAPE: the 6-way form selector (circle / triangle / square / pentagon / hexagon / octagon), quantised + latched per shape at spawn; ROT: the bipolar live-global spin (center = still, ± = CW/CCW); RATE: KNOB-ONLY internal clock (0 = spawn ONLY on gate events; turning up spawns faster, hard-capped at 1 shape / 500 ms). Inputs — gate (a rising edge spawns one shape), collide (a LIVE gate: HIGH = shapes bounce off each other elastically, LOW/unpatched = pass through), d / v / spd / decay / shape / rotation (per-param CV; shape latches at spawn, rotation is the live-global spin), video (sampled by the mapped output). Outputs (4, all derived from a per-pixel overlap-COUNT of the active shapes — using the rotated polygon coverage — each shape's contribution scaled by its DECAY fade alpha so a fading shape counts less / draws lighter): OVERLAP (mono-video) white wherever ≥1 shape covers the pixel (dimming as the covering shape fades), black else; CONTOUR (mono-video) shape OUTLINES only, ring width = 10% of that shape's diameter (min 2 px) so many shapes read as "ripples in a pond" (rings lighten as they decay); COMBINE (colour video) the overlap region colourized by overlap COUNT through a hue ramp (1 overlap = one hue; 2,3,4… cycle the spectrum) with brightness + saturation rising as more shapes stack and dimming as the stack fades; MAPPED (colour video) shows the VIDEO input's contents wherever ≥2 shapes overlap, black elsewhere. Bounded sim: shapes bounce forever and accumulate, so a hard cap of 200 active shapes culls the OLDEST first (a safety net even with DECAY) to keep per-frame cost bounded. Determinism: the random spawn position + each shape's seeded initial rotation angle come from a seeded mulberry32 PRNG (fixed default seed; never Math.random), so VRT / per-port / behavioral sweeps are reproducible. Usage: patch a clock/sequencer GATE in (or just turn RATE up) and route OVERLAP / CONTOUR to a SCOPE or mono consumer, COMBINE to OUTPUT for the coloured stack, and a video source → VIDEO in + MAPPED → OUTPUT to punch that source through the ≥2-overlap region. Pick a SHAPE for the spawn (polygons + ROTATION give kaleidoscopic spinning-outline fields), leave DECAY at 0 for an accumulating static field or turn it up for a trail/dissolve look, and gate the COLLIDE input HIGH (any clock/gate/LFO over the high threshold) to switch the field from a soft overlapping wash into a billiards-like cluster where the shapes knock each other around.
outputs
overlap mono-videocontour mono-videocombine videomapped video
inputs
d cvv cvspd cvdecay cvshape cvrotation cvvideo video
params
d 0..1v 0..1spd 0..1decay 0..1shape 0..1rotation 0..1rate 0..1freeze 0..1
example js
spawn('outlines', 'x');
set('x', 'd', 0);
patch('x.overlap', 'x.d');
PAINTER — an MS-Paint-style drawing surface as a video SOURCE. The card is a tiny Windows-95 Paint: a tool grid (PENCIL = hard 1px, BRUSH = round sized stroke, ERASER = paints the background colour, LINE, RECT, ELLIPSE, FILL = flood fill, EYEDROPPER = pick a colour off the canvas, TEXT = stamp a string), the classic 28-colour Win95 palette (left-click a swatch = FOREGROUND, right-click = BACKGROUND), a SIZE slider (brush/line width 1..48), a FILL toggle (outline vs background-filled rect/ellipse), and an engine-resolution drawing canvas. Whatever you paint is the single video OUTPUT in real time: the card binds its live canvas to the module once and the engine uploads that canvas every frame (a 1:1 mapping — the canvas IS the frame). MODEL: the drawing is a Y.Doc-synced ordered op log (node.data.ops) — each committed stroke / shape / fill / text appends one PaintOp; on mount and on any remote edit the card REPLAYS the log onto the canvas (deterministic, pure painter-draw.ts), so every rack-mate paints the same picture. Tool / colour / brush-size are LOCAL per-collaborator (only the drawing syncs). UNDO pops the last op; CLEAR empties the log to a blank white page. IO — Inputs: none (a pure source). Output: out (video) — the painted canvas at the engine output resolution. The OUT port lives in the card's yellow drill-down PATCH PANEL (no raw side jacks, #767 standard). A freshly-spawned node renders a blank WHITE page (MS-Paint's default), so it is never a dead black frame. USAGE: paint a logo / title card / doodle and route OUT into a mixer / keyer / effect / OUTPUT; collaborate on one canvas in a shared rack.
outputs
example js
spawn('painter', 'x');
peakstate is a self-running mandala/kaleidoscope generator — a video SOURCE with no video input. An internal "pen" traces a deterministic drifting Lissajous path (penAtTime: x = 0.5·cos(0.7t), y = 0.5·sin(1.3t + 0.4·cos(0.3t))) through a centred unit disc, pushing one sample per frame into a 600-sample ring buffer (~10s of comet trail). Each frame the whole trail is redrawn once per kaleidoscope arm — rotated by 2π/complexity and mirrored about the arm axis — over a translucent black overlay that decays the previous frame, giving the classic mirror-arm bloom. MOVE + OBLONG add a slow spirograph orbit of the mandala's centre (period ~20s at Speed 1): MOVE sets orbit radius, OBLONG squashes the orbit's vertical extent from a circle toward a near-horizontal "rolling tube". The module emits three coherent views of the SAME pen trail with different palette/transform. Usage: drop it in for a generative kaleidoscope bloom, patch an LFO or envelope into the CV jacks to pulse the speed/arm-count/hue, and pick the mono, full-colour, or pseudo-3D output to suit the look.
outputs
mono_out mono-videorgb_out videoout_3d video
inputs
speed_cv cvcomplexity_cv cvcolor_speed_cv cv
params
speed 0.1..4complexity 4..32color_speed 0..4move 0..1oblong 0..1
example js
spawn('peakstate', 'x');
set('x', 'speed', 0);
patch('x.mono_out', 'x.speed_cv');
PEERTUBE — federated-video SOURCE. Search the open PeerTube fediverse, pick a video, and its picture streams in as a CLEAN (untainted) WebGL video texture plus stereo audio — a real downstream-usable source, NOT play-only. MODEL: a debounced search box queries Sepia Search (sepiasearch.org, the official PeerTube meta-index — CORS-open + anonymous, NO proxy needed); results list title, channel@host, duration, a LIVE badge, and a thumbnail. Click a result → the card fetches that video's per-instance public API (https://<host>/api/v1/videos/{uuid}) and resolves a playable stream: it PREFERS the HLS master playlist (streamingPlaylists[0].playlistUrl → attached via hls.js for adaptive playback) and FALLS BACK to the highest-resolution progressive MP4 (files[].fileUrl, attached as a plain <video src>). The stream attaches to a card-owned <video crossorigin="anonymous"> → the engine samples it into the FBO (the `video` output) + taps stereo audio (audio_l / audio_r via MediaElementSource → ChannelSplitter, with the shared keep-alive so an unpatched source keeps decoding at full rate). WHY THE TEXTURE + AUDIO ARE CLEAN (verified): PeerTube sends Access-Control-Allow-Origin:* on the FINAL media hop (master .m3u8 + the fragmented-mp4 / mpeg-ts segments) under a favorable `credentialless` COEP posture, so a crossorigin <video> fed by hls.js both PLAYS and yields an untainted texture — unlike archive.org video (play-only). IO — Inputs: play_trigger (gate, edge=trigger — a rising edge toggles play/pause), next_trigger (gate, edge=trigger — a rising edge loads the next search result, wrapping). Outputs: video (the live frame texture), audio_l / audio_r (stereo, silent ConstantSource placeholders until a stream attaches), loaded (trigger — one pulse when a new video finishes loading), ended (trigger — pulses when the video reaches its end), playing (gate — HIGH while actually playing), playhead (CV — 0..1 normalized position). CV/PATCHING: all inputs + outputs live in the card's yellow drill-down PATCH PANEL (top-left/top-right affordances → INPUT/OUTPUT → grouped Gates / CV / Audio / Video rows) — there are NO raw side jacks (#767 standard). Trigger inputs are main-thread edge-detected the established video-module way (a single bridge-written cv-param read per tick — never a whole-AnalyserNode rescan). USAGE: type a term, press Enter (or wait for the debounce), pick a video, and route VIDEO into a mixer/keyer/OUTPUT and audio_l/audio_r into AUDIO OUT or a SCOPE/SYNESTHESIA for audio-reactive visuals; clock the next_trigger from a sequencer to channel-surf the fediverse. GRACEFUL: ~1/6 instances misconfigure CORS (raw S3 with no ACAO) → the element taints / the HLS load fails fatally → the card degrades to "display unavailable", surfaces a clear status, and AUTO-SKIPS to the next result (it never crashes, taints the texture, or hangs on loading). An optional instance field biases attribution display. Only { instanceHost, uuid, name, selectedHost } persist on the node + sync to rack-mates (everyone resolves + plays the same video locally); transient playback state stays render-local (never a per-frame synced-store write). The AUDIO TRAP: the <video> is created muted for autoplay, then un-muted ONLY after the MediaElementSource tap succeeds, so audio routes into WebAudio without native speaker double-output. LEGAL: federated PUBLIC videos, not hosted by patchtogether — an in-card disclaimer + attribution to PeerTube + Sepia Search ship with it.
outputs
video videoaudio_l audioaudio_r audioloaded gateended gateplaying gateplayhead cv
inputs
play_trigger gatenext_trigger gate
params
gain 0..2cv_play_trigger 0..1cv_next_trigger 0..1
example js
spawn('peertube', 'x');
set('x', 'gain', 0);
patch('x.video', 'x.play_trigger');
Five-voice polyphonic analog-style synth. A POLY input (the polyPitchGate chord bus from MIDI LANE / POLYSEQZ / a chord sequencer) drives five band-limited VCO voices — lane i → voice i — each with TUNE (±36 st) / FINE (±100 ¢) / exponential FM / through-phase PM / pulse width and a continuous tri→saw→square WAVE morph. Each voice has its own gated amplitude envelope, but the A/D/S/R is SHARED across all five voices (one device-level ADSR; aligned with CUBE / WAVECEL / DX7); the gate edge comes from that voice's poly lane, and a released voice holds the played pitch through its release tail. The five post-envelope voices sum through a stereo mixer (per-voice LEVEL + equal-power PAN) and an embedded multimode filter — a continuous LP→BP→HP→Notch MODE dial on a TPT state-variable filter (CUTOFF / RESONANCE) with a WET/DRY bypass — to the stereo OUT_L / OUT_R pair. Each voice is also tapped pre-mixer (post-envelope) to a VOICE1..VOICE5 mono output, and each voice has its own audio-rate FM jack (fm1..fm5) that feeds both its FM and PM depths. 48 panel params (5 voices × 8 + 4 shared ADSR + 4 filter). DSP is own-code: a clean-room polyBLEP band-limited oscillator, a Cytomic/Zavalishin TPT state-variable filter, and a linear-ADSR envelope — not a port of any copyleft source (permissive only).
outputs
out_l audioout_r audiovoice1 audiovoice2 audiovoice3 audiovoice4 audiovoice5 audio
inputs
poly polyPitchGatefm1 audiofm2 audiofm3 audiofm4 audiofm5 audio
params
attack 0.001..5sdecay 0.001..5ssustain 0..1release 0.001..5scutoff 20..20000Hzresonance 0..0.99mode 0..1wetdry 0..1
example js
spawn('pentemelodica', 'x');
set('x', 'attack', 0.001);
patch('x.out_l', 'x.poly');
Image-file SOURCE with a 7-SLOT ASSET SELECTOR. Click "Choose image…" to load a single picture (downscaled to 1024×768, JPEG-encoded, base64 → node.data and synced to all rack-mates; each peer decodes it back into the WebGL2 source texture). A GAIN knob (CV-modulatable, 0..2) scales the output RGB; output `out` is a video-domain image source. ASSET SELECTOR: right-click the card to open "Load multiple…", a 7-row panel where each row is labelled with a note (C D E F G A B) and loads its own image into one of 7 slots (all 7 base64 images sync + all 7 textures stay resident in GPU memory). A clip player (or any pitch + gate source) then SWITCHES which slot is displayed: patch the clip player's GATE output → ASSET GATE and its PITCH output → ASSET PITCH. On each ASSET GATE rising edge the module reads ASSET PITCH (raw V/oct), maps it to a slot by PITCH CLASS (octave-independent), and instantly shows that slot if it holds an image. THE 7-NOTE → SLOT TABLE (the default clip's in-key rows, C-major from C3): C3 (MIDI 48) → slot 1, D3 (50) → slot 2, E3 (52) → slot 3, F3 (53) → slot 4, G3 (55) → slot 5, A3 (57) → slot 6, B3 (59) → slot 7. Matching is by pitch class, so a C in ANY octave selects slot 1, a B in any octave selects slot 7, etc. A pitch whose class is a black key (C# D# F# G# A#) maps to NO slot → the event is IGNORED (the current image keeps showing). The displayed selection is LOCAL render state (every peer computes it from the same synced gate + synced images), so it is never written to the Y.Doc per gate event. ASSET PITCH/ASSET GATE + GAIN live in the card's yellow drill-down PATCH PANEL (no raw side jacks, #767 standard). Limits: 8 PICTUREBOX per workspace.
outputs
inputs
gain cvasset_pitch pitchasset_gate gate
params
gain 0..2asset_pitch -10..10asset_gate 0..1
example js
spawn('picturebox', 'x');
set('x', 'gain', 0);
patch('x.out', 'x.gain');
Modal / sympathetic-string resonator (Mutable Instruments Rings archetype). Faithful TypeScript port of the eurorack/rings/ DSP (MIT-licensed). v1 ships two resonator models: (0) MODAL — bank of 24 parallel stiffness-stretched RBJ bandpasses with cosine-weighted Odd/Even pickup taps; (1) SYMPATHETIC — 2 parallel Karplus-Strong delay lines with one-pole damping. STRUCTURE/BRIGHTNESS/DAMPING/POSITION are the canonical Rings knobs; LEVEL is a soft-limited output gain. EXCITER in drives both engines; STRUM rising edge re-ignites a ~10ms noise burst (KS) or impulse (modal). Outputs odd / even — patch both for stereo. Polyphony 1; STRING+REVERB deferred.
outputs
inputs
in audiopitch pitchstrum gatemodel_cv cvnote_cv cvstr_cv cvbright_cv cvdamp_cv cvpos_cv cvlevel_cv cv
params
model 0..?note -60..60ststructure 0..1brightness 0..1damping 0..1position 0..1level 0..1
example js
spawn('rings', 'x');
set('x', 'model', 0);
patch('x.odd', 'x.in');
Loop-based sample player. Upload an audio file (≤2 MB — wav / mp3 / m4a / ogg / flac / opus) or record from patched audio inputs in place; the clip is decoded to mono and played back from a fractional read-cursor with linear interpolation, so a single rate control covers varispeed including reverse. IDLE-BY-DEFAULT: a freshly loaded sample sits SILENT and does NOT auto-play — and a saved patch reloads idle too. Playback is started by a TRIGGER, which is MODE-AWARE: in one-shot mode (1-SHOT) a trigger plays the sample through once then returns to silence; in loop mode (LOOP) a trigger starts looping (a re-trigger restarts from the window edge). The trigger comes from BOTH the TRIG gate input (a rising edge) AND the on-card TRIGGER button (a momentary pulse to the worklet that works whether or not a cable is patched into TRIG). The RATE slider spans −2 (reverse 2×) through +1 (forward unity, the centered no-op) to +2 (forward 2×), and a rate CV input sums on top (±1 V = ±100%). START / END faders set the playback window. Holds exactly one sample at a time — a new upload or recording replaces the previous buffer (no playlist, no slots), which keeps the per-instance memory ceiling deterministic.
outputs
inputs
trig gaterate_cv cvstart_cv cvend_cv cvaudio_l_in audioaudio_r_in audio
params
rate ?..?mode 0..1start ?..?end ?..?poly 0..1
example js
spawn('samsloop', 'x');
set('x', 'rate', 0);
patch('x.out', 'x.trig');
SHAPEDRAMPS is a sync-locked parametric ramp generator for video coordinate synthesis — it draws gradients across the raster rather than processing an incoming picture. It emits four mono-video ramps: h_lin/v_lin are stable identity ramps (red channel = screen u or v, untouched by any knob or CV) for clean raster passthrough into geometry modules like RUTTETRA; h_out/v_out are shaped ramps that morph through four canonical shapes via H Shape / V Shape (0..1): linear (t), triangle (abs(2t-1)), soft-fold (0.5 - 0.5*cos(2pi*t)), and radial (H = distance from center scaled so corners read 1, V = angle around center), blending linearly between adjacent shapes. H/V Phase shift the ramp before shaping; H/V Freq scale the axis variable and fract-wrap it, so freq=2 repeats the shape twice across the canvas (a triangle becomes a two-peak zigzag). It also packs two general-purpose 2-channel video crossfade mixers (out = (1-amount)*A + amount*B) so you can blend ramp shapes without an external V-MIXER, though they accept any mono-video signal. Typical use: feed h_lin/v_lin into a coordinate consumer for an identity raster, then crossfade toward h_out/v_out to warp the geometry; all four ramps and both mixers render every frame so downstream consumers always read fresh textures.
outputs
h_lin mono-videov_lin mono-videoh_out mono-videov_out mono-videomix1_out mono-videomix2_out mono-video
inputs
h_shape cvv_shape cvh_phase cvv_phase cvh_freq cvv_freq cvmix1_a mono-videomix1_b mono-videomix2_a mono-videomix2_b mono-videomix1_cv cvmix2_cv cv
params
h_shape 0..1v_shape 0..1h_phase 0..1v_phase 0..1h_freq 0.5..8v_freq 0.5..8mix1 0..1mix2 0..1
example js
spawn('shapedramps', 'x');
set('x', 'h_shape', 0);
patch('x.h_lin', 'x.h_shape');
SHAPES is a procedural geometry source: it has no video input and synthesizes a mono-video stream entirely in its fragment shader. Each frame the shader evaluates a signed-distance field for one of three primitives — a circle, a square, or an equilateral triangle pointing up — and renders it white-on-black, antialiased with a soft edge band. The shape is picked discretely (the Shape value is rounded to the nearest integer; there is no morph or blend between primitives). The frame's UV coordinates are rotated and divided by the zoom factor before the SDF is evaluated, so larger zoom grows the shape's footprint while rotation spins it about its cell center; the antialiasing band is scaled by 1/zoom so the outline stays crisp even when the shape fills the frame. With Tile off the whole frame is a single cell holding one centered shape; with Tile on the frame is repeated (via fract of the UVs) into a Grid×Grid array of identical cells, each carrying its own centered copy. Use it as a clean mask/matte or pattern generator feeding compositors, displacement, or feedback stages; patch CV into shape/tile/rotate/zoom to animate the geometry from the audio side.
outputs
inputs
shape cvtile cvrotate cvzoom cv
params
shape 0..2tile 0..1tileN 1..16rotate -3.14159..3.14159zoom 0.05..10
example js
spawn('shapes', 'x');
set('x', 'shape', 0);
patch('x.out', 'x.shape');
SIX STRUM — a 6-string guitar/bass/harp INSTRUMENT: six of our KARPLUS extended-Karplus-Strong string voices side by side, each behind its own amplitude ADSR, summed to MONO through a tuning-matched resonant BODY. Three ways to play it, all into one engine: STRUM by hand via six edge-trigger inputs NORMALLED low→high (patch only strum #1 to barre all six with one gate; patch #1 and #4 for two independent strum groups; STRUM SPREAD + DIR set the roll speed and down/up/alternate direction); play polyphonically from the POLY input (the 16-lane polyPitchGate cable — lanes 1–6 → strings 1–6, note-on plucks, note-off releases); or feed one root to the mono CHORD pitch-CV input and it voices a real guitar chord across the strings (each string takes the lowest chord-tone at/above its open pitch; the CHORD selector picks maj/min/dom7/maj7/min7/sus4/power5/octaves). Six MUTE gates model a finger laid loosely on a string — while high that string goes dead and near-unpitched (a palm-mute thunk; MUTE depth sets how dead), gate all six to choke the chord. GUITAR/BASS/HARP are NOT hidden presets: they are knob states of this ONE engine — TUNING (open-string set: guitar EADGBE / a low six-string bass / a diatonic harp), REGISTER (octave), RING (string sustain, calibrated to real plucked-string decay research — ~2.5 s guitar, long dark bass, very long harp), MATERIAL (nylon↔steel), PICK POS/TONE/GRAIN (where + how hard it is plucked), STIFF (inharmonicity toward wound/metal), plus SPREAD (per-string detune + always-distinct excitation seeds so a barre chord choruses instead of phase-combing) and BODY (the box resonance). Per-string ADSR: fast ATTACK, SUSTAIN held at 1 so the string's own physical ring IS the sustain, RELEASE bites on note-off / mute. ACCENT is a shared per-hit velocity CV (louder + brighter), plus a per-knob CV input on the voice + strum controls (tone_cv/grain_cv/spread_cv/body_cv sweep PICK TONE/GRAIN, SPREAD, BODY full-range around the knob; strum_cv sweeps the STRUM roll; the DISCRETE dir_cv/chord_cv quantize a −1..+1 CV across the DIR 0..2 and CHORD 0..7 selector indices). Mono out; no per-voice controls beyond the shared panel. Card: a STRUM audition button barres all six strings; the rear PatchPanel groups the inputs per string (strum + mute) plus POLY / CHORD / ACCENT and the PICK · STRUM CV modulators.
outputs
inputs
poly polyPitchGatechord pitchstrum1 gatestrum2 gatestrum3 gatestrum4 gatestrum5 gatestrum6 gatemute1 gatemute2 gatemute3 gatemute4 gatemute5 gatemute6 gateaccent cvtone_cv cvgrain_cv cvspread_cv cvbody_cv cvstrum_cv cvdir_cv cvchord_cv cv
params
register -24..24string 0.1..10smaterial 0..1pickPos 0.02..0.5stiffness 0..1pickTone 0..1pickGrain 0.1..4attack 0.0005..5senvDecay 0.001..5ssustain 0..1release 0.001..5smuteDepth 0..1strumSpread 0..1strumDir 0..2spread 0..1body 0..1level -24..12dBtuning 0..2quality 0..7
example js
spawn('sixstrum', 'x');
set('x', 'register', 0);
patch('x.out', 'x.poly');
Deep, flexible stereo SNARE VOICE with a polyphonic two-hand DRUMROLL — the mate to KICK DRUM (own-code). Four decoupled acoustic layers the way a real snare works: a HEAD modal bank at inharmonic Bessel-zero ratios (1.0/1.03/1.593/2.135 — the pitchless membrane "thunk", self-ringing Chamberlin resonators struck by an impulse, with a downward pitch-drop at the strike), band-passed noise BODY around the head (TONE crossfades HEAD↔BODY), the SNARE-WIRE buzz — the defining sizzle — as bright HP-tunable noise on a SHARED re-excitable bed that breathes with the rectified head displacement AND rings out between strokes, and a short CRACK stick-contact transient. TWO strike inputs into one synth: trigger_in fires ONE hit per rising edge; gate_in runs the drumroll WHILE high. The roll is genuinely POLYPHONIC — two alternating hands 180° interleaved, each stroke (and each buzz sub-stroke) allocating its own voice from a 10-voice lowest-energy-steal pool while the shared wire bed sustains continuity, so overlapping decaying tails SUPERPOSE into a continuous roll (NOT a pulsed one-shot retrigger). ROLL SPEED sets 4–24 strokes/hand (+ roll_speed_cv at 1V/oct), BOUNCE morphs single-stroke → double/open → dense multi-bounce buzz/press roll (a coefficient-of-restitution bounce train, ≤6 sub-strokes, more as the hands slow), HUMANIZE adds seeded (deterministic) timing/velocity/detune jitter, and a hard per-second allocation budget routes excess buzz density into bed re-excitation only. The summed pool + bed run ONE shared bus: an oversampled DRIVE with a single HARD character switch (clean-warm tanh @2× vs aggressive wavefold+asym @4×), a 20 Hz DC block, and a per-channel true-peak CEILING soft-clip. Stereo: SPREAD pans the two hands (constant-power) + detunes them, WIDTH decorrelates only the bright wire sizzle; head/body stay centered so width=0 AND spread=0 → L==R exactly (mono-safe fold-down). Inputs: trigger_in (edge-trigger STRIKE), gate_in (edge-gate DRUMROLL), roll_speed_cv, accent_in (per-hit velocity + drive/level macro, latched at each strike), pitch_cv (1V/oct whole-voice transpose), choke_in (level-sensitive hand-on-head mute), PLUS a per-control CV input for each of the 21 remaining voice knobs (tune_cv … level_cv — each ±1 sweeps its param full-range around the live knob, cvScale matched to the knob curve), alongside the existing roll_speed_cv. Level −24..+12 dB, guarded by the ceiling. Wide 3u banded card: HEAD·BODY·WIRE / CRACK·ROLL·DRIVE / STEREO·OUT. Ships as a clean, punchy snare; hold a long gate for a continuous two-hand roll.
outputs
audio_l audioaudio_r audio
inputs
trigger_in gategate_in gateroll_speed_cv cvaccent_in cvpitch_cv cvchoke_in gatetune_cv cvhead_decay_cv cvdamping_cv cvdamp_cv cvpitch_amt_cv cvpitch_time_cv cvtone_cv cvbody_decay_cv cvwire_cv cvwire_tone_cv cvwire_decay_cv cvcrack_cv cvcrack_tone_cv cvbounce_cv cvhumanize_cv cvdrive_cv cvhard_cv cvceiling_cv cvspread_cv cvwidth_cv cvlevel_cv cv
params
tune 90..400Hztone 0..1damping 0..1head_decay 30..600msbody_decay 20..300mspitch_amt 0..12stpitch_time 3..80mswire 0..1wire_tone 1500..9000Hzwire_decay 40..700mscrack 0..1crack_tone 800..7000Hzdamp 0..1roll_speed 0..1bounce 0..1humanize 0..1spread 0..1drive 0..1hard 0..1ceiling 0..1width 0..1level -24..12dB
example js
spawn('snaredrum', 'x');
set('x', 'tune', 180);
patch('x.audio_l', 'x.trigger_in');
SPIROGRAPHS — a classic-spirograph video GENERATOR (a pure synth SOURCE: no video input). It draws 1–3 INDEPENDENT spirograph curves — HYPOTROCHOIDS (the rolling circle rolls INSIDE the fixed one) or EPITROCHOIDS (OUTSIDE) — each with its OWN full parameter set + matching CV, each DRIFTING around the screen with its fixed circle bouncing off the frame edges like a real spirograph pinned to the page. THE CURVES (a pen at offset p in a rolling circle of radius r rolling on/in a fixed circle of radius R): hypotrochoid x=(R−r)cos t + p·cos(((R−r)/r)t), y=(R−r)sin t − p·sin(((R−r)/r)t); epitrochoid x=(R+r)cos t − p·cos(((R+r)/r)t), y=(R+r)sin t − p·sin(((R+r)/r)t). The parameter t sweeps over exactly enough revolutions to CLOSE the figure — derived from the reduced R:r ratio (revolutionsToClose = the denominator of R/r in lowest terms); irrational-ish ratios are CAPPED at a sane maximum so the curve dense-fills the annulus instead of running forever. COUNT (1..3, DISCRETE knob + CV) sets how many spiros render. Each spiro i∈{1,2,3} owns ten params (port/param id sI_<name>), EACH with a knob AND a CV input: fixedRadius (R), rollingRadius (r), penOffset (p), inside (0=epitrochoid/outside, 1=hypotrochoid/inside — a discrete toggle), rotation, scale, xOffset, yOffset, thickness (real px line-width), and chroma (a colorwheel HUE for that spiro). MOTION: each spiro's CENTER drifts on its OWN per-spiro velocity/phase (the three never move in lockstep); the FIXED-radius circle (R scaled to screen) is CONSTRAINED to stay fully inside the frame and rolls/BOUNCES (elastic reflection) off the perimeter when it hits an edge — only the fixed circle's center+R is bound-constrained (closed-form, deterministic), while the drawn CURVE may extend past the viewport and clip (desired). The reflect/bounce + the curve math live in the pure, unit-tested $lib/video/modules/spirographs-math. RENDERING: Canvas2D polylines (real line-width with round joins/caps) painted to an OffscreenCanvas and uploaded as a GL texture each frame (the SHAPEGEN/TEXTMARQUEE path) — the right tool for thick stroked curves, where a GLSL distance-field would be costlier and read worse. OUTPUTS (all video): out (video) — the full-COLOUR composite, each spiro in its chroma hue additively blended on black so crossings glow; mono_out (mono-video) — every spiro stroked WHITE on black, a clean matte for keying / luma effects (reachable via read('outputTexture:mono_out')); overlap (video, labelled CANDY) — a COLOUR-OVERLAP output: the per-pixel overlap DENSITY (how many lines stack there — self-crossings AND multiple spiros) is colour-mapped into a rainbow that CASCADES with the count (deep cool hue for one line, racing through green→yellow→red→magenta as lines pile up) and blooms toward a white candy core where many overlap — gooey "candy" goodness (reachable via read('outputTexture:overlap')). CARD: a live preview of the colour OUT, the COUNT knob, a 1/2/3 SPIRO SELECTOR that swaps the knob bank to that spiro, an INSIDE/OUTSIDE toggle, a CHROMA colorwheel, and the per-spiro fader bank. All ports live on the yellow drill-down PATCH PANEL (no raw side jacks); the drill-down GROUPS the CV inputs per-spiro (a count section + spiro1 / spiro2 / spiro3 sections). USAGE: dial COUNT, pick a spiro tab, tune R/r/pen for the figure (low gcd ratios make few-lobed flowers, coprime ratios dense rosettes), set INSIDE/OUTSIDE + chroma, then patch out → OUTPUT / a video mixer / an effect — or modulate any per-spiro param from an LFO/sequencer for an animated, drifting spirograph.
outputs
out videomono_out mono-videooverlap video
example js
spawn('spirographs', 'x');
Buchla 259-style complex waveform generator — the "swole VCO" of the lineup: a primary oscillator and a sine modulator in one module. The primary blends saw → triangle → square via the SYMMETRY control, then passes through a West-Coast wavefolder (FOLD). TIMBRE is audio-rate cross-modulation: the modulator FMs the primary (up to ~±4 semitones of deviation) for the classic Buchla harmonic complexity. Pitch is 1 V/oct (0 V = C4) with tune/fine knobs. Outputs the folded primary, the raw modulator, and a summed mix, plus a mono-video SCOPE output of the primary waveform for cross-domain video patching. Pure Web Audio, modeled after ILLOGIC's structure.
outputs
out audiomod_out audiosum_out audioscope mono-video
inputs
pitch pitchmod_pitch pitchfm audiotimbre cvsymmetry cvfold cvratio cv
params
tune -36..36stfine -100..100¢mod_tune -36..36stmod_fine -100..100¢ratio 0..8timbre 0..1symmetry 0..1fold 0..1
example js
spawn('swolevco', 'x');
set('x', 'tune', 0);
patch('x.out', 'x.pitch');
TEXTMARQUEE — a rich-text MARQUEE video GENERATOR (source). MODEL: you type a styled paragraph in the card's tiny rich-text editor (a contenteditable region + a small toolbar); it serializes into a small RICH-TEXT MODEL — an array of paragraphs, each a list of styled RUNS `{ text, bold, italic, underline, color }` + a paragraph `align` (left/center/right), plus a layer FOREGROUND (default glyph colour) + BACKGROUND fill. That model persists in node.data.richText (Y.Doc-synced) and is the single source of truth for BOTH the editor DOM and the video texture. CONTROLS (card): the toolbar = ALIGN left/center/right, BOLD, ITALIC, UNDERLINE, per-character TEXT COLOR (pick a colour for the current selection), and the layer FG + BG colour swatches; below the editor are four knobs (ScrlX/ScrlY/PosX/PosY) and a live preview of the OUT layer. RENDERING: the model is laid out + drawn to an OFFSCREEN 2D canvas with SYSTEM FONTS (real glyphs — system-font text cannot be rasterized in GLSL, so a 2D-canvas→texture upload is the clean path: measure each run in its font/weight/style, draw with per-run colour + underline, honour align + bg fill), uploaded as a WebGL texture, and drawn into the module's FBO at a SCROLL offset + screen POSITION — a 90s-screensaver marquee. A freshly-spawned node renders a "textmarquee" placeholder until you type. I/O: OUT (video) — the rendered scrolling text layer. CV (each port id == param id, linear cvScale so a bipolar ±1 source sweeps the param's full range centred on the knob): ScrlX / ScrlY — horizontal / vertical scroll SPEED (BIPOLAR knob; 0.5 = static, <0.5 scrolls one way, >0.5 the other; the text wraps + re-enters from the opposite edge — a continuous ribbon). PosX / PosY — raw screen POSITION 0..1, CALIBRATED so 0 = text fully off one edge, 1 = fully off the other, 0.5 = centred (drawX = -textWidth + posX*(screenW+textWidth)); with the default-centred knob a bipolar LFO patched into PosX/PosY sweeps the text ALL THE WAY across — fully off the left, through centred, to fully off the right, and back. USAGE: type + style your banner, set FG/BG, then either crawl it with ScrlX/ScrlY (a scrolling marquee) or sweep PosX/PosY from an LFO/sequencer for a CV-driven swoosh; patch OUT into OUTPUT, a video mixer, or any video effect to title/overlay another layer. All ports live on the yellow drill-down PATCH PANEL (no raw side jacks). The pos/scroll/wrap math + the rich-text layout/measurement are pure, unit-tested helpers in $lib/video/modules/textmarquee-layout.
outputs
inputs
scrollX cvscrollY cvposX cvposY cv
params
scrollX 0..1scrollY 0..1posX 0..1posY 0..1
example js
spawn('textmarquee', 'x');
set('x', 'scrollX', 0);
patch('x.out', 'x.scrollX');
TIDY VCO — flagship virtual-analog SUBTRACTIVE SYNTH VOICE: two polyBLEP oscillators with a continuous saw↔pulse SHAPE morph per osc, shared PW + audio-rate PWM CV, OSC2 octave switch + cents detune, and a −1-octave sub square, into an ALL-NEW nonlinear zero-delay-feedback DIODE LADDER filter (EMS VCS3 / TB-303 lineage — bidirectionally-coupled stages, soft warm knee into 24 dB/oct, feedback squelch limiter for a bounded near-sine self-oscillation, CUTOFF calibrated to the resonant pitch within 3 cents so keytracked self-osc plays in tune, DRIVE tanh saturation at 2× oversampling, part-compensated squelch bass dip). Two RC-curve "punch" ADSRs (CEM3310-style exponential segments, overshoot-target convex attack, analog resume-from-level retrigger): a filter EG with bipolar ±4-octave ENV amount + keytracking, and an amp EG into an OTA-flavored soft-knee VCA whose tanh bias blooms gentle even harmonics with level. 5-voice POLY on the polyPitchGate chord bus (lane i → voice i, release tails hold their pitch) AND a mono pitch/gate pair driving a REAL 2-voice unison (±7¢·WIDTH drift panned to opposite sides — true stereo beating); WIDTH also fans the poly voices across the stereo field (root anchored center). Stereo out_l/out_r pair (auto-wires R when L is patched), HOLD pad for droning the voice from the card, and a CV jack for EVERY control (27 inputs): the six original CVs — cutoff (4 oct/V, audio-rate), res, pwm (audio-rate), drive, fold (audio-rate), sym (audio-rate) — PLUS a GLOBAL block-rate per-knob CV for each remaining control: shape1/shape2/mix/sub/fsus/sus/track/width (0..1 full-swing), detune (±50¢), oct2 (discrete ±1 step/V), env (±4-oct bipolar), the six EG times (4 oct/V), and level (±18 dB/V). Every law is scaled in the shared core (lib/tidy-vco-dsp.ts) so cv = 0 is a byte-exact no-op. The rear patch panel groups its jacks into OSC·WAVEFOLD·DIODE FILTER·FILTER EG·AMP EG·OUT·POLY/OUT sections mirroring the card headers.
outputs
inputs
poly polyPitchGatepitch cvgate gatecutoff_cv cvres_cv cvpwm_cv cvdrive_cv cvfold_cv cvsym_cv cvshape1_cv cvshape2_cv cvdetune_cv cvoct2_cv cvmix_cv cvsub_cv cvenv_cv cvtrack_cv cvfatk_cv cvfdec_cv cvfsus_cv cvfrel_cv cvatk_cv cvdec_cv cvsus_cv cvrel_cv cvwidth_cv cvlevel_cv cv
params
shape1 0..1shape2 0..1pw 0.05..0.5detune -50..50¢oct2 -1..1mix 0..1sub 0..1fold 0..1sym -1..1cutoff 40..14000Hzres 0..1drive 0..1env -1..1track 0..1fatk 0.0005..5sfdec 0.001..5sfsus 0..1frel 0.001..5satk 0.0005..5sdec 0.001..5ssus 0..1rel 0.001..5swidth 0..1level -24..12dBhold 0..1
example js
spawn('tidyVco', 'x');
set('x', 'shape1', 0);
patch('x.out_l', 'x.poly');
Analog-modeled TOM DRUM voice — the third member of the drum family (KICK DRUM / SNARE DRUM), at deliberately CURATED complexity: one synthesis engine, seven voice knobs + level, spanning the classic analog tom lineage in one continuous space (808-woody bridged-T ring, 909-punchy swept osc + overtone, Simmons SDS-V "piuuu" dive-bomb, deep floor tom). DSP (own-code, packages/dsp/src/lib/tomtom-dsp.ts): a MEMBRANE fundamental (Tune 60–400 Hz) plus the inharmonic 1.593× Bessel second mode (TONE tilts fundamental ↔ overtone, with its own faster decay) riding ONE exponential pitch-BEND envelope (Bend 0–24 st depth — 0 = stable pitch, 24 = a two-octave zap; B Time 10–300 ms), a band-passed BREATH noise layer tracking ~2.5× the settled pitch (NOISE balances membrane ↔ breath, the SDS-V tone/noise mix law — 808 breath at low settings, full Simmons noise hit at the top), a 2×-oversampled warm-tanh DRIVE, 20 Hz DC block, dB LEVEL, and a true-peak tanh bound (|out| < 1 always). DECAY (40–1500 ms) is FREQUENCY-COMPENSATED: the −60 dB ring time is set in ms regardless of Tune, so a 60 Hz floor tom and a 400 Hz timbale ring equally long at the same knob. Inputs: trigger_in (edge-trigger STRIKE — phases reset, envelopes fire), accent_in (per-hit 0..1 CV latched at the strike edge: up to +80 % velocity AND +50 % bend depth, like a harder stick), pitch_cv (1V/oct whole-voice transpose incl. the breath band), bend_cv (±24 st/V into Bend — full-swing), decay_cv (2 oct of decay TIME per volt), tone_cv / noise_cv (sum into the balances). A per-knob CV now covers EVERY continuous control: tune_cv (2 oct/V on the TUNE knob — distinct from the whole-voice pitch_cv), bend_time_cv (2 oct of sweep-settle time/V), drive_cv (sums into DRIVE), and level_cv (±18 dB/V — the full 36 dB range centered on the knob). Each law is scaled in the shared core so cv = 0 is a perfect no-op. Output: ONE mono audio_out. Card: compact MEMBRANE·COLOR·OUT fader band + a press-to-fire STRIKE pad (the `strike` param, OR-ed with trigger_in in the worklet — one hit per press edge); the rear patch panel groups its jacks into MEMBRANE·COLOR·OUT·TRIG/OUT sections mirroring the card headers. Control set modeled on the Vermona DRM1 tom channel (tune / bend / decay / noise / drive).
outputs
inputs
trigger_in gateaccent_in cvpitch_cv cvbend_cv cvdecay_cv cvtone_cv cvnoise_cv cvtune_cv cvbend_time_cv cvdrive_cv cvlevel_cv cv
params
tune 60..400Hzbend_amt 0..24stbend_time 10..300msdecay 40..1500mstone 0..1noise 0..1drive 0..1level -24..12dBstrike 0..1
example js
spawn('tomtom', 'x');
set('x', 'tune', 110);
patch('x.audio_out', 'x.trigger_in');
Multi-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).
outputs
inputs
example js
spawn('toybox', 'x');
patch('x.out', 'x.inA');
TB-303 voice slice — clean-room TypeScript port of the voice subset of Robin Schmidt's Open303 (MIT, https://github.com/RobinSchmidt/Open303). 6 canonical 303 knobs (TUNE ±12 st, CUTOFF 40 Hz – 6 kHz, RESONANCE 0..1, ENVELOPE 0..1, DECAY 50 ms – 3 s, ACCENT 0..1) plus pitch / gate / accent_in audio-rate inputs and per-knob CV. The DSP is the TB_303 mode of rosic::TeeBeeFilter (the diode-feedback ladder with feedback HP — NOT a moogafakkin ladder; that is the whole point of Open303), the rosic::DecayEnvelope on cutoff, a simplified AR amp envelope mirroring rosic::AnalogEnvelope, and a polyBLEP saw replacing the BlendOscillator wavetable (the 303 character lives in the filter, not the oscillator). Cutoff is modulated per-sample via Open303's measured-mapping scaler+offset formula. All 6 knobs have an 80 Hz one-pole WtParamSmoother on the audio thread (per PR #435) so knob drags and CV ride pop-free through the steep filter. Accent boosts both amp peak and filter env contribution on accented notes. The full 404 module — sequencer + transpose + slide + waveform switch + TD-3 smiley — is queued as a follow-up.
outputs
inputs
pitch_in pitchgate_in gateaccent_in gatetune_cv cvcutoff_cv cvres_cv cvenv_cv cvdecay_cv cvaccent_cv cvwaveform_cv cv
params
tune -12..12stcutoff 40..6000Hzresonance 0..1envelope 0..1decay 50..3000msaccent 0..1waveform 0..1
example js
spawn('treeohvox', 'x');
set('x', 'tune', 0);
patch('x.audio_out', 'x.pitch_in');
International live-TV source. Pick a country on the 2D world map (or the country list) → a channel list (filtered to playable HLS streams) → tune a channel and its live picture streams in as an UNTAINTED video texture (validated under the app's COEP require-corp headers: famelack HLS plays + yields a clean WebGL2 texture, so VIDEO out is a real downstream-usable texture, not play-only) plus stereo audio_l/audio_r from the stream's audio track. The "random" button (and the random/next CV trigger inputs) jump channels for happy-accident channel-surfing. Gate outputs: channel_changed pulses on each tune (trigger), stream_online holds high while the stream actually plays (gate). Dead/geo-blocked/unlicensed-pulled streams fail cleanly → marked "unavailable" + auto-skipped, never a hang or a tainted texture. Channel selection persists on the node + syncs to rack-mates (everyone tunes to the same stream). LEGAL: streams are THIRD-PARTY public streams, NOT hosted by patchtogether — this is a player pointed at the same iptv-org-derived directory many "free live TV" sites use; an in-card disclaimer + attribution (Famelack, MIT-licensed dataset fetched at runtime; iptv-org) ship with it, geo-blocked entries are honored/marked, and dead links are filtered.
outputs
video videoaudio_l audioaudio_r audiochannel_changed gatestream_online gate
inputs
params
gain 0..2cv_next 0..1cv_random 0..1
example js
spawn('tvLibrarian', 'x');
set('x', 'gain', 0);
patch('x.video', 'x.next');
A HOST module that runs a loaded `.vfpga` declarative effect spec — a "virtual FPGA bitstream" swapped into one reconfigurable card (inspired by, not a clone of, classic video-synth hardware). The module declares the full I/O SUPERSET it can wire — 4 video inputs (vin1..vin4), 4 CV inputs (cv1..cv4), 4 gate inputs (g1..g4), 2 video outputs (vout1 canonical / vout2 via read('outputTexture:vout2')), and an 8-slot generic param bank (p1..p8) — and a loaded VfpgaSpec (node.data.vfpga, picked from the "load preset…" menu) selects which subset is ACTIVE and what GL render-graph runs. The card is manifest-driven: it renders the full port superset as handles (inactive ones dimmed) and shows only the loaded spec's active CV inputs (each with a bipolar SCALE attenuverter + OFFSET + always-on scope), gate inputs (with an activity LED), and its mapped param knobs (p1..pN, labelled + ranged by the spec, MIDI-learnable). CV inputs are linear-scaled into named role uniforms; gate inputs raw-pass into synthetic gN_evt params that the factory hysteresis edge-detects (DOOM/backdraft convention) so a spec's shader can read a held level / rising-edge count. Specs are IN-REPO bundled TypeScript (no user-uploaded code in v1) collected by import.meta.glob; the render runs on the MAIN thread (#1811 — the render worker has no cross-thread input textures for vin1..vin4, its return protocol carries one picture per node so vout2 could not be served, and the card polls read('gateState') every frame). Preset change hot-swaps the GL pipeline. This foundation ships ONE VFPGA: smpte-bars, a pure SMPTE-style colour-bar generator (0 video in → 1 video out, one CV SHIFT role + BRIGHT/SAT params) — a deterministic always-on reference source for bringing up downstream effects.
outputs
example js
spawn('vfpgaRunner', 'x');
videobox is a local-file VIDEO PLAYER: you drop (or pick) a video file from disk and it decodes the file each frame into the module's video output, while the file's stereo audio track is split out to the audio_l / audio_r jacks for patching back into the audio domain. The card owns the actual HTMLVideoElement and object-URL; the engine samples that element through a decode-rate frame uploader (only re-uploading when a genuinely new frame lands, downscaled to the engine resolution) so playback stays smooth even at 1080p. Behind the file picker the card uses File System Access handles (Chromium) to remember your pick and one-click-reload it next session; other browsers and other peers fall back to a "Re-link: drop <name>" prompt. The playhead is multiplayer-synced: play, pause and seek write a shared (isPlaying / lastSyncTime / lastSyncPosition) triple to the node so every peer's local copy follows, drift-correcting whenever it slips more than ~0.5s off the expected position. The card is drag-resizable from the bottom-right corner (whole-rack-unit tiles, default and minimum 360x360) so several videoboxes can be tiled into a wall of TVs; size persists on the node and syncs to peers, and the 16:9 video preview grows to fill the resized card. Right-click the preview for Fullscreen (a LOCAL per-peer state, NOT multiplayer-synced) or Full Frame (in-app, the video consumes the whole card, hiding the title/picker/transport/seekbar; double-click to exit), where ONLY Full Frame is synced to peers. Usage: drop a clip, hit Play, and patch video into a mixer/output and audio_l/audio_r into your audio chain; pulse the TRIG input from a clock or button to toggle play/pause hands-free. Idle (no file) shows a faint blue gradient so an empty card reads as alive-but-empty.
outputs
video videoaudio_l audioaudio_r audio
inputs
params
gain 0..2cv_play_trigger 0..1
example js
spawn('videobox', 'x');
set('x', 'gain', 0);
patch('x.video', 'x.play_trigger');
VIDEOCUBE — the VIDEO version of the audio CUBE oscillator. It ingests THREE 60-frame video rings — A (FLOOR), B (WALL/connector), C (CEILING) — each generated LIVE from a connected video input or loaded from a .frametable.png atlas, and combines them exactly the way audio CUBE combines its three wavetables: an OCCUPANCY-WEIGHTED trilinear morph. For every output pixel it reads a colour from each ring, derives an occupancy position from the WALL luma, and blends the three by the SAME cube-dsp occ weights — MORPH cross-fades A↔C through B, CONNECT (+ CONNECT STRENGTH) reshapes/swells the interior binding, CRUSH posterizes colour depth, SPACE CRUSH mosaics the sampling grid, SPACE DIFFUSE smears the coords, MATERIAL switches soft-blend↔hard mosaic and WRAP mirror-folds — so video_out is a recognizable MORPHED IMAGE of the sources. It ALSO emits AUDIO: the same three rings are reduced to luma heightfields and scanned by the IDENTICAL cube-dsp surface-height slice the audio CUBE plays, so audio_out is a mono wavetable-oscillator drone whose timbre is driven by the SAME knobs (MORPH/CONNECT/CRUSH/SPACE/slice Y·ROT/WRAP/MATERIAL) — the picture and the sound move through one shared field structure. TUNE/FINE set pitch, LEVEL the gain, FOLD a west-coast wavefolder on the audio (audio-only). A global READER (SMOOTH/MORPH/CHAOS) reads each ring's 60-frame history, FREEZE holds the live rings and LIVE forces the real-time read. Beyond video_out + audio_out it exposes SIX dedicated slice-visualizer video jacks — SCOPE (the 256-sample surface-height wave the audio plays), SLICE (the 2-D cutting-plane cross-section), DEPTH (the intersection-depth heightmap, bright=deep/loud), and a SMOOTH/MORPH/CHAOS triptych (the cross-section forced through each reader treatment) — each a different readout of the SAME field/slice, each PER-PORT gated so an unpatched jack costs ZERO GPU, and the SLICE VIEW toggle (TEXTURED/XRAY/WEIGHTS) recolours slice+triptych. Rendered at half engine resolution with a renderer-gated tap count (the FrameTable budget ×3); the audio slice is GPU-reduced + recomputed off the audio thread on a throttle. v1 ships MONO-DRONE-first (poly/ADSR = follow-up). The occupancy combine + colour blend + luma reduction are a 1:1 CPU mirror unit-tested in $lib/video/videocube-core, reusing the audio-CUBE field math (cube-dsp) wholesale. All ports live on the yellow drill-down PATCH PANEL. Look-affecting new WebGL shader — held for owner visual preview.
outputs
video_out videoaudio_out audioscope_out videoslice_out videodepth_out videosmooth_out videomorph_out videochaos_out video
inputs
video_a videovideo_b videovideo_c videomorph_cv cvconnect_cv cvconnect_strength_cv cvcrush_cv cvspace_crush_cv cvspace_diffuse_cv cvslice_y_cv cvslice_rx_cv cvslice_ry_cv cvslice_rz_cv cvview_x_cv cvview_y_cv cvfold_cv cvspread_cv cvscan_cv cvtune_cv cvchroma_depth_cv cvmotion_cv cvhue_mode_cv cv
params
tune -36..36stfine -100..100¢morph_fc 0..1connect 0..1connect_strength 0..1crush 0..1space_crush 0..1space_diffuse 0..1fold 0..1spread 0..1scan 0..1chroma_depth 0..1motion 0..1slice_y 0..1slice_rx -3.1416..3.1416slice_ry -3.1416..3.1416slice_rz -3.1416..3.1416level 0..2view_zoom 0.3..3view_rot_x -3.1416..3.1416view_rot_y -3.1416..3.1416view_rot_z -3.1416..3.1416wrap 0..1material 0..1screen_on 0..1reader_mode 0..2freeze 0..1live 0..1slice_view 0..2hue_mode 0..1
example js
spawn('videocube', 'x');
set('x', 'tune', 0);
patch('x.video_out', 'x.video_a');
Local-file VIDEO player with a PERFORMANT varispeed transport AND a 7-SLOT ASSET SELECTOR. SINGLE VIDEO: pick/drop a video (objectUrl + optional FileSystemFileHandle for one-click reload; collaborators re-link their own copy — the bytes stay local, only fileMeta syncs); the frame texture is sampled off requestVideoFrameCallback so the `video` output streams at ANY playback speed (the #291 fix). Transport: SPEED knob (0=-4×…0.5=+1×…1=+4×; reverse scrubs at a throttled ~10 Hz), a START/END window, LOOP vs ONE-SHOT, and rising-edge gates START / PAUSE / RESET / LOOP. Stereo `audio_l` / `audio_r` bridge the file's audio (with a silent keep-alive so an unpatched source keeps decoding at full rate). ASSET SELECTOR: right-click the card → "Load multiple…", a 7-row panel (notes C D E F G A B) that loads up to 7 videos, one per slot; all 7 are PRELOADED as separate <video> elements (first frame decoded) so a switch is instant. Patch a clip player's GATE → ASSET GATE and PITCH → ASSET PITCH: on each ASSET GATE rising edge the module reads ASSET PITCH (raw V/oct), maps it to a slot by PITCH CLASS, and — if that slot holds a loaded video — makes it the active source, RESTARTS IT FROM THE BEGINNING (currentTime=0), plays it (if the transport is playing) under the current speed/window/loop settings, and re-wires its audio to the now-active element. THE 7-NOTE → SLOT TABLE (the default clip's in-key rows, C-major from C3): C3 (MIDI 48) → slot 1, D3 (50) → slot 2, E3 (52) → slot 3, F3 (53) → slot 4, G3 (55) → slot 5, A3 (57) → slot 6, B3 (59) → slot 7. Matching is octave-independent (a C in any octave → slot 1, …); a black-key pitch (C# D# F# G# A#) maps to NO slot → the event is IGNORED (current video keeps playing). MEMORY: 7 preloaded <video> elements are heavy, so each slot's file is capped at 100 MB. The displayed selection is LOCAL render state (computed from the synced gate + per-slot fileMeta), never written to the Y.Doc per gate event. ASSET PITCH / ASSET GATE + the transport gates/CV all live in the card's yellow drill-down PATCH PANEL (no raw side jacks, #767 standard). CROP: a second video output `crop` re-samples a boxed sub-region of the `video` output, scaled up to the full output resolution (a live zoom). Press "add crop" on the card to overlay a resizable, aspect-locked rectangle (thin red border, dimmed outside) on the preview — drag inside to move, drag a corner to resize; the box always keeps the current OUTPUT aspect (16:9 or 4:3) and re-fits when the aspect flips. The rect is stored normalized on the node (x,y,w in 0..1, height derived from the aspect; synced + undoable; ONE per node, unaffected by slot switches). Until a crop is added — or after "remove crop" — the `crop` output passes the full frame through unchanged, so it never goes black and always streams. Reusable crop core ($lib/video/crop-core + crop-render + ui/video/CropOverlay) so other video modules can adopt the same Crop output later.
outputs
video videocrop videoaudio_l audioaudio_r audio
inputs
cv_start gatecv_pause gatecv_reset gatecv_loop_toggle gateasset_pitch pitchasset_gate gatespeedCv cvstartCv cvendCv cv
params
speed 0..1start 0..1end 0..1speedCv -1..1startCv -1..1endCv -1..1cv_start 0..1cv_pause 0..1cv_reset 0..1cv_loop_toggle 0..1asset_pitch -10..10asset_gate 0..1
example js
spawn('videovarispeed', 'x');
set('x', 'speed', 0);
patch('x.video', 'x.cv_start');
Plays one of YOUR installed instrument plugins (AU builds of your VSTs) as a first-class rack voice via the vst-bridge native helper — poly/gate/vel CV converted to sample-accurate MIDI in the worklet, plugin audio returned into the lane, native editor window on your machine.
outputs
inputs
poly polyPitchGatepitch cvgate gatevel cv
example js
spawn('vstInstrument', 'x');
patch('x.out_l', 'x.poly');
Stereo wavetable oscillator with morph, stereo spread, and a West-Coast wavefolder — a more advanced sibling of WAVETABLEVCO. MORPH scans the wavetable frame position, SPREAD widens the stereo image by reading NEIGHBOURING frames into progressively harder-panned taps (a wavetable spread, not a pitch detune — one shared phase), FOLD adds wavefolding harmonics. Ships factory wavetables and accepts runtime upload of E352-format WAV wavetables (frames ride the Y.Doc out to every rack-mate). The card offers a 3D wavetable visualization in addition to the standard scope view. A POLY input (polyPitchGate) accepts the 5-voice chord bus from MIDI LANE (mode=poly) or POLYSEQZ: when any lane is gated WAVECEL renders one wavetable voice per gated lane at that lane's pitch and sums them — the morph/spread/fold timbre is shared across all voices. With nothing patched to poly the mono `pitch` path runs unchanged. A per-voice amplitude ADSR (Attack / Decay / Sustain / Release) plus a BASE VOL knob shape each voice. GATING is decided by what is PATCHED: when the POLY bus OR the mono TRIGGER is connected, WAVECEL is a GATED voice — a lane/voice sounds only while it is gated-or-releasing, and a never-gated lane is SILENT (patching poly never auto-drones). When NEITHER is patched, WAVECEL is a continuous raw VCO. BASE VOL is a per-voice VCA FLOOR the envelope rides on top of: gain = base + (1-base)·env per ACTIVE voice — base=0 (the default) is pure ADSR (silent between notes), 0.5 floors at 0.5 and rises to 1.0 at the env peak, base=1 means the env does nothing (full gain while active). It applies ONLY to gated voices: the raw-VCO case (nothing patched) runs at unity independently of BASE, so the legacy continuous drone is byte-identical at every setting. In poly mode each lane's gate edge drives its own envelope (one envelope per voice, soft/click-safe retrigger — re-gating a still-releasing voice attacks from the current level, never pops); the mix is normalized over ACTIVE voices (1/sqrt(N)) so a sustain=0 held note doesn't pump the level and a releasing tail doesn't pop. The ADSR + BASE VOL params read live (continuous k-rate) across all stages, so a held chord rides sustain/release in real time and a fresh note attacks at the value present at its trigger moment. Edge detection is block-rate (retrigger granularity floor ≈ one audio block); connectedness (poly/trigger patched) is read from the live patch edges, not bus presence.
outputs
out_l audioout_r audioscope_out mono-videowave3d_out video
inputs
pitch pitchfm audiomorph_cv cvspread_cv cvfold_cv cvpoly polyPitchGatetrigger gate
params
tune -36..36stfine -100..100¢morph 0..1spread 1..5fold 0..1attack 0.001..5sdecay 0.001..5ssustain 0..1release 0.001..5sbase_vol 0..1
example js
spawn('wavecel', 'x');
set('x', 'tune', 0);
patch('x.out_l', 'x.pitch');
Hybrid 4-oscillator 3D video synth. Four wavetable "wall oscillators" sit on the faces of a 3D unit box, each emitting a colored wave ribbon (RED / GREEN / BLUE / ALPHA) that points into the box; a single user camera renders the scene, positioned via an XY pad (X/Y) and a HEIGHT slider (Z). Audio out is the sum of the four oscillators, each weighted by its per-osc ADSR and by camera↔source distance — the distance gain is the single source of truth shared by audio and visuals, so "closer = louder = bigger ribbon" stays consistent across both domains. Per oscillator: tune/fine, wavetable morph, stereo spread, wavefolder, thickness, and ADSR; camera zoom + Y-rotation shape the view. All four oscillators run in one worklet for a tight audio-rate path. Six cross-domain VIDEO WALL inputs (wall1–wall6) texture an upstream video module onto the six faces of the room (FRONT/BACK/LEFT/RIGHT/FLOOR/CEILING) seen from inside; each face has a TRANSPARENCY knob (0–100%) blending the wall over the scene and a DISTORT knob (0–1) that morphs the flat wall into a convex dome we look up into. Patch the video output back into a wall input for recursive video feedback.
outputs
L audioR audioout_red audioout_grn audioout_blu audioout_alp audiovideo_out mono-video
inputs
gate1 gatepitch_cv1 cvgate2 gatepitch_cv2 cvgate3 gatepitch_cv3 cvgate4 gatepitch_cv4 cvmorph1_cv cvmorph2_cv cvmorph3_cv cvmorph4_cv cvpos_x cvpos_y cvpos_z cvzoom cvrot cvscale cvwiggle cvalpha_in videowall1 videowall2 videowall3 videowall4 videowall5 videowall6 video
example js
spawn('wavesculpt', 'x');
patch('x.L', 'x.gate1');
Wavetable oscillator that morphs saw -> square -> triangle -> sine across a 16-frame table.
outputs
inputs
pitch pitchfm audiowavePos cvpm audiotune cvfine cvfmAmount cvpmAmount cv
params
tune -36..36stfine -100..100¢wavePos 0..1fmAmount -1..1pmAmount -1..1
example js
spawn('wavetableVco', 'x');
set('x', 'tune', 0);
patch('x.audio', 'x.pitch');