ichor

A collaborative, hackable, browser-native synthesizer that grows with you.

ichor is a browser-native synthesizer built in Svelte 5 + SvelteKit. Two oscillators with swappable engines (subtractive, FM, AM, Karplus-Strong), a multi-mode filter, draggable LFOs, an effects rack, an arpeggiator, live coding via Strudel, and real-time collaborative sessions over Cloudflare Durable Objects. Nothing to install. Bring headphones.

Synthesis

  • Two oscillators with swappable engines: BASIC (subtractive), FM, AM, and PLUCK (Karplus-Strong)
  • Waveforms: sine / square / saw / triangle / pulse (with adjustable width)
  • Per-osc octave / semi / fine detune, level, pan, enable
  • Unison (1–7 voices) with stereo spread for fat supersaws (BASIC engine)
  • Sub oscillator (1 or 2 octaves below) for added low-end weight
  • Noise generator (white / pink / brown) with level + pan
  • AHDSR envelope with a draggable visual editor and matching knobs
  • Multi-mode filter (lowpass / highpass / bandpass / notch) with cutoff + resonance
  • LFO → cutoff with rate, depth, shape (sine / square / saw), enable
  • Voice modes: poly / mono / legato / scale, with glide time
    • Glide implemented via parallel mono Tone.Synth instances (PolySynth doesn't support portamento)
    • Pluck voicing uses a per-note PluckSynth Map (PluckSynth isn't Monophonic)

Effects rack (right drawer)

  • Distortion, Bitcrusher, Chorus, Ping-Pong Delay, Reverb
  • Each with wet/dry mix and individual enable; chained left-to-right
  • All effect params are addressable from Strudel patterns

Performance & I/O

  • Multi-octave keyboard (3 octaves) with mouse glissando, two rows of keybinds (a–j / k–'), z / x to shift octave
  • Pitch bend wheel (±2 semitones, springs back) and mod wheel (boosts LFO depth)
  • Web MIDI input support

UI

  • Live oscilloscope + spectrum analyser as the page header background
  • 300+ built-in presets in a floating, physics-based picker (drag, hover, click to load)
  • Live JSON patch editor on the left, Strudel + Effects drawers on the right — all built on CodeMirror 6 with custom Catppuccin highlighting
  • Three Catppuccin themes: latte, mocha, sky
  • Beginner's guide modal: 15 lessons with embedded interactive visualizers (knob, waveform, envelope, filter)
  • Lucide icons throughout

Networking & live coding

  • Live multiplayer sessions via Cloudflare Durable Objects: click "share" → get a URL → anyone who opens it edits the same patch in real time, with presence count
  • Live coding via a custom Strudel lookahead scheduler (bypassing broken @kabelsalat internals): write patterns like note("c3 eb3 g3 bb3").s("ichor").cutoff(sine.range(0.1, 0.9)) in the side drawer; every synth + effect param is a Strudel-addressable channel.

Stack

  • Svelte 5 (runes) + SvelteKit
  • TypeScript + Zod (canonical patch schema, validated at every write)
  • Tailwind CSS v4 (Catppuccin tokens via @theme inline)
  • Tone.js on top of the Web Audio API
  • Cloudflare Workers (adapter: sveltekit-adapter cloudflare+cfTarget:workers)
  • Vite for dev & build
  • ESLint + Prettier

Getting started

pnpm install
pnpm dev            # http://localhost:5173
pnpm dev -- --open

Build

pnpm build
pnpm preview

Run locally only

It's a static-friendly SvelteKit app; pnpm preview is enough for local-only usage. No external services are required for layers 0–3.


Project layout

src/
  lib/
    audio/
      patch.ts          # Zod schema, types, defaults, validators
      engine.svelte.ts  # AudioEngine: Tone graph, validated setters,
                        # subscribe API, write-source tagging
      presets.ts        # Built-in presets grouped by category
    server/
      PatchSession.ts       # Durable Object: holds canonical patch, fans out updates
    audio/
      session.svelte.ts     # Client-side websocket sync, coalesced per-frame
    audio/
      strudel.svelte.ts     # Custom lookahead scheduler + param routing
    ui/
      Keyboard.svelte       # Multi-octave keyboard, glissando, octave shift
      SoundDesign.svelte    # Two-row grid: osc1 / osc2 / filter then env / lfo
      OscCard.svelte        # Per-osc swappable engine UI (BASIC/FM/AM/Pluck)
      Envelope.svelte       # Draggable AHDSR + knob row, ResizeObserver-sized SVG
      LFOWave.svelte        # Inline LFO shape preview
      Knob.svelte           # Circular knob (drag, wheel, dblclick, exponential curves)
      Visualizer.svelte     # Canvas oscilloscope + spectrum, theme-aware
      ThemeToggle.svelte    # latte / mocha / sky picker
      PatchEditor.svelte    # CodeMirror 6 JSON editor in a sliding drawer
      StrudelDrawer.svelte  # CodeMirror 6 live coding drawer
      EffectsDrawer.svelte  # Distortion / bitcrusher / chorus / delay / reverb
      PitchModWheels.svelte # Pitch bend (sprung) + mod wheel
      PresetCloud.svelte    # Verlet-physics preset picker
      Guide.svelte          # 15-lesson beginner modal w/ live visualizers
      guide/                # GuideKnob, GuideWave, GuideEnvelope, GuideFilter
  routes/
    +page.svelte         # Layout: full-bleed visualizer header → controls → keys
    layout.css           # Catppuccin tokens (latte / mocha / sky)

Roadmap

Built in public, in small increments. The rough direction, loosest to firmest:

  • next: patch persistence (save your own presets to localStorage / cloud)
  • sampling (mic, file, tab capture)
  • export (WAV / MP3 / stems / MIDI)
  • granular / FM / wavetable synths
  • AI helpers (vibe → arrangement, "more space", etc.)
  • accessibility, gamepad / Pi controllers
  • guided exploration & genre templates, Synplant-style sound exploration

Nothing here is committed to. It's a list of things that would be cool, in roughly the order they make sense to build. Sequencing is intentionally out of scope — that lane belongs to Strudel.


Why "ichor"

The fluid in the veins of the gods. Short, looks good in lowercase, and the domain was free.


License

MIT.

Built With

Share this project:

Updates