Public and inspectable work

Synth of Doom

A playable polyphonic browser synthesiser, rebuilt from a preserved 2017 Angular/Web Audio project into a typed, accessible React application through a Codex-assisted modernisation.

Role
Creator and developer
Period
2017–present
Status
Active public project
Focus
React, TypeScript, Web Audio API

A project worth preserving

I began this browser synthesiser in 2017 as an Angular 4 and Web Audio learning project. It grew quickly into something musically substantial: three oscillators, coloured noise, dual filters and envelopes, freely routed LFOs, delay, computer-keyboard input, Web MIDI, and MIDI learn.

The name is a reference to Camel of Doom, the long-running musical project I have worked on for more than 25 years—and the same family joke behind my camelofcode GitHub name. The software has always been a meeting point between engineering, formal audio-programming knowledge, and practical experience making music.

By 2026, the original application was also an unusually complete time capsule. Its Angular 4, TypeScript 2.5, SystemJS, Gulp, Express/Pug, jQuery, generated JavaScript, Visual Studio artefacts, and vendored dependencies filled more than 1,500 tracked files. The application itself was much smaller, but the build was no longer reproducible on a current toolchain, the fixed-width interface was inaccessible, and there were no meaningful automated tests.

I renamed the existing repository rather than creating a history-free replacement. The complete original implementation remains available at the annotated legacy-angular tag, pointing to its untouched January 2019 baseline.

Preserve the behaviour, replace the coupling

Before changing the application, I used Codex to help audit the old source and document its signal path, control ranges, intentional behaviour, and defects. I deliberately did not reinstall the obsolete dependency stack merely to make it run once; the source and history were sufficient to establish the behavioural baseline safely.

The modern application is a ground-up implementation rather than a component-for-component Angular conversion. React owns presentation and interaction, but it never owns raw audio nodes or constructs the signal graph during render. A framework-independent TypeScript engine accepts typed note, parameter, and patch commands and exposes small serialisable state snapshots to the interface.

A central registry defines 63 stable synthesiser parameters, including ranges, defaults, units, scaling curves, display formatting, modulation eligibility, and MIDI-learn behaviour. The interface, presets, MIDI mappings, validation, and audio engine therefore share one source of truth rather than repeating subtly different control rules.

A real polyphonic synthesiser

The modern engine creates sound entirely through the browser's Web Audio API rather than playing samples or calling a server. Each voice contains three oscillators, coloured noise, source mixing, two parallel filters with independent envelopes, and a velocity-sensitive amplitude envelope. Up to eight voices feed a shared output path with three assignable LFOs, filtered feedback delay, saturation, compressor protection, master gain, and a live oscilloscope.

Turning the voice count down to one preserves useful monophonic last-note priority and portamento behaviour. Higher settings provide independently enveloped polyphony with deterministic voice allocation and stealing. Loading a preset replaces and disposes the complete graph, preventing delay memory, modulation connections, scheduled automation, or running sources from leaking between patches.

One note path for every way of playing

Pointer keys, the computer keyboard, and Web MIDI all feed one ownership-aware note router. One input source cannot release a note still held by another, while blur, visibility changes, device disconnection, and all-notes-off handling prevent stuck notes.

MIDI support is treated as a progressive enhancement. The application handles explicit permission, unsupported and denied states, input selection, hot-plugging, device-scoped notes, and reconnects. Any learnable control can be assigned to a MIDI CC through a stable parameter ID; mappings are collision-aware and persist locally.

An accessible instrument rather than a picture of one

The original on-screen keyboard was visual only, and its custom controls had no keyboard or screen-reader behaviour. The modern piano is generated from note metadata and genuinely plays. Custom knobs use native slider semantics with visible values, accessible names, arrow/Page/Home/End control, reset gestures, focus styling, and pointer or touch dragging.

The full interface adapts from a twelve-column desktop instrument to tablet and narrow-screen layouts without horizontal page overflow. It includes reduced-motion and forced-colour support, never relies on colour alone for state, and exposes audio and MIDI status as text. Automated axe-core checks supplement keyboard, responsive, and real-browser interaction tests.

Presets and safe state

Five factory patches demonstrate mono bass, lead, polyphonic pad, noise percussion, and the initial signal path. Users can create, rename, duplicate, delete, import, and export their own patches. Patch data, selected preset, accent colour, and MIDI mappings are versioned and stored locally; malformed or unsupported data fails safely rather than leaving a broken graph or blank application.

Every change—whether it comes from the interface, a MIDI control, or a loaded preset—travels through the same validated action path. The serialisable patch, rendered controls, and live engine cannot quietly drift apart.

A concentrated Codex-assisted rebuild

The public history makes the modernisation unusually inspectable. The first planning commit landed on 4 November 2026; the React/Vite foundation, domain model, audio engine, unified inputs, accessible controls, responsive interface, presets, polyphony, browser hardening, documentation, and initial merge followed across 4–5 November. The CI-complete merge arrived roughly 22 hours after the plan was committed, with later screenshot documentation added separately.

Git records the sequence and results; my first-hand account identifies Codex as the implementation and review collaborator. The result is more important than commit volume: the repository contains a clear migration plan, preserved baseline, focused architectural boundaries, a reproducible static build, and verification at several levels.

The current suite contains 75 Vitest unit/component tests and seven Playwright journeys. Those journeys exercise native Web Audio startup and recovery, pointer and computer-keyboard notes, real parameter-to-GainNode wiring, preset persistence, responsive layout, and automated accessibility checks. CI repeats the browser layer in Chromium, Firefox, and WebKit. The complete local quality gate—formatting, strict type-checking, linting, tests, browser checks, and production build—passes from the public repository.

Why I include this work

  • Preserving the history and useful intent of an old system instead of disguising a rewrite as a new greenfield project.
  • Turning an obsolete framework-coupled implementation into a framework-independent, strictly typed audio engine.
  • Applying real audio and music knowledge to scheduling, signal flow, modulation, envelopes, polyphony, and safety.
  • Designing one parameter and state model across UI controls, MIDI, presets, validation, and the live audio graph.
  • Building accessible custom interaction rather than accepting that specialist creative software must exclude keyboard or assistive-technology users.
  • Directing a fast Codex-assisted modernisation while retaining explicit architecture, tests, browser evidence, and an honest historical baseline.