Releases: MrityunjayBhardwaj/SonicWeb
Release list
v1.5.0-beta.4
Prerelease. First release under the new name @mjayb/sonicweb (formerly @mjayb/sonicpijs). The project is now Sonic Web, live at sonicweb.cc. Bundles all work merged since beta.3. Install: npm install @mjayb/sonicweb.
Rebrand
- Sonic Pi Web → Sonic Web. npm
@mjayb/sonicpijs → @mjayb/sonicweb, repoSonicPiWeb → SonicWeb, domainsonicpi.cc → sonicweb.cc(308 redirect, old paths preserved). Upstream Sonic Pi attribution and the audio-criticalsonic-pi-*synthdef/OSC identifiers are unchanged. (#610, #611, #614)
Engine
- Self-sufficient by default (#604) — the engine auto-loads SuperSonic (the GPL scsynth WASM core, from CDN), the tree-sitter transpiler, and its PRNG table itself. A bare consumer needs zero runtime wiring.
Hash#length/#size/#counttranspile correctly via a runtime__spSizehelper (#603) — fixes loops over Hash literals silently dropping layers.spread(hits, total)matches Desktop Sonic Pi'sredistributeplacement byte-for-byte for all(hits, total)(#597, #598).- Parity fixes: dynamic / non-literal
use_synth, inline-loop RNG continuity, cross-loopget/settime-frame alignment, and bang-only mixer/volume linting (#585, #586, #588–#593, #595, #596).
Docs & tooling
- Parity dashboards and every docs code snippet are now playable (Run/Stop) with an "open in sonicweb.cc" link; dashboards published to Vercel + Cloudflare R2.
- Fixed the "Engine not ready" race when Run is pressed during engine init.
- CI:
actions/checkout/actions/setup-nodebumped to v6/v7 (#607).
v1.5.0-beta.3 — engine hardening + beta-ready docs
v1.5.0-beta.3 — engine hardening + beta-ready docs
First public-beta cut since v1.5.0-beta.2 (Apr 10). ~30 PRs of engine
fixes, ~50 new DSL fns, full parity-comparator tooling, and the
operational docs to triage incoming beta bugs.
TL;DR — what's in this release
Engine fixes (the 2026-05 audit pass)
- SP72:
use_bpm/use_synthno longer leak fromin_threadinto
nestedlive_loopregistration (#280) — months of FX-comparator
numbers were running web at half tempo - SP78–81: four hot-swap state-hygiene bugs caught by Playwright +
WAV reproducers (#283) — empty-cycle Updates, FX-scope inversion,
stop-tail queue drain, per-loop state leakage - SP70: lossless capture pipeline —
Recorder.tsswapped MediaRecorder- opus codec for raw float32 → WAV, removing the codec damage that
had been masquerading as "filter family broken" for weeks
- opus codec for raw float32 → WAV, removing the codec damage that
- Per-loop audio isolation via private loopBus + monitor synth (#178)
— first Sonic Pi implementation to ship this - #199: nested
live_loophot-swap routes throughscheduler.hotSwap()
directly (#261) - Sonic Tau mixer baseline:
pre_amp=0.3,amp=0.8— calibrated for
browser-WASM headroom, not Desktop SP driver levels
DSL coverage: ~87% of upstream (148/170 user-facing fns)
- Tier A: 17 critical fns — recording, kill, current_*, define
persistence (#213) - Tier B: 27 fns across 3 PRs — ring/spread, tuplets,
defonce,
sync_bpm, dynamic eval (run_code/eval_file/run_file),
live_audiocontrol,load_example(#230, #234, #238) - Tier C: 23 fns across 3 PRs — state wrappers
(with_synth_defaults,with_sample_defaults,use_arg_checks
family), sample/buffer registry, mixer + introspection helpers
(#252, #254, #256) - Help panel + autocomplete entries shipped for every new fn
Parity tooling
- A/B comparator rig (
tools/capture-desktop.ts+ spectrogram
compare) — captures both engines, renders 3-panel mel-spectrograms,
reports L2 + MFCC + per-beat metrics (#264) - FX A/B inspector:
npm run inspectopens a single-file viewer with
desktop ↔ web<audio>players, sync-play, MFCC sidebar, verdict
tiers HIGH/MID/LOW/INCONCLUSIVE (#277) - WAV-verify sweep across 40 wired FX (#275) — current scores:
4 HIGH / 26 MID / 8 LOW / 2 INCONCLUSIVE (composite ~89.5%) - Capture tool:
--batch <dir>for fixture-folder runs (#224),
engine hooks forputs/cues instead of DOM slicing (#222)
UI / app
- Welcome buffer drop + Cm9 finale (#220)
- Preloader pulls forward CDN deps before App mount (#284)
- Live Mixer Pre-Amp / Amp sliders in Prefs push to scsynth on drag
- MIDI toolbar button hidden until Tier D ships (Web MIDI input via
the engine API still works) - README rebranded to "Sonic Pi Web"
Beta operational infrastructure
KNOWN_ISSUES.md+TRIAGE.md+ bug-template gates so triage
isn't ambiguous when users report problems- 8 Plausible custom events wired for beta-visibility analytics
- SuperSonic CDN version pinned end-to-end (v0.57.0)
Numbers
- 929 unit tests pass (was 805 at beta.2)
- 62/66 synths working end-to-end (3 upstream WASM LOAD-FAIL)
- 197/197 samples
- 42/42 FX wired, 40/42 WAV-classified
- ~148/170 DSL fns (~87%)
- 49 community forum compositions verified end-to-end in Chromium
Compatibility notes
- Mixer behaviour change: web output is ~0.19× Desktop SP's
driver-attenuated level by default. Comparators should RMS-normalise
before A/B. Override via Prefs → Audio → Mixer Amp. - MIDI: Web MIDI continues to work via the engine API. Only the
toolbar dropdown UI is gated until Tier D (midi_pc,midi_raw,
midi_sysex, device picker). - DSL: net additive — no upstream-compatible code that worked in
beta.2 should break in beta.3.
Install
npm install @mjayb/sonicpijs # latest tag now points here
npm install @mjayb/sonicpijs@beta # beta tag — same versionBrowser: https://sonicpi.cc
See KNOWN_ISSUES.md
for current beta blockers.
v1.5.0-beta.2 — transpiler consolidation + P2 credibility
Prerelease. Two PRs since v1.5.0-beta.0 — transpiler stability work (#165) and four new features from the Desktop Sonic Pi tutorial (#166). Installed via npm install @mjayb/sonicpijs@beta.
New features
use_real_time(tutorial Ch 11.1) — disable the schedule-ahead buffer for the current thread. Use inside a MIDI-triggeredlive_loopto drop perceived latency to near-zero.- MIDI input cue paths (tutorial Ch 11) —
sync "/midi:*:1/note_on"(Desktop format) andsync "/midi/note_on"(short format) both match a single incoming MIDI message. Either form from the tutorial now works unchanged. - Wildcard sync / cue matching (tutorial Ch 10.3) —
sync "/osc*/trigger"andsync "/foo/?ar/tick"support*and?glob patterns. Exact-match is still the fast path when no glob characters are present. synth :sound_in/synth :sound_in_stereo(tutorial Ch 13.1) — capture microphone input into the live composition. The browser prompts for mic permission on first use, then routes the mic through scsynth so you can gate, filter, FX, or record it like any other synth. Stereo input devices go throughsound_in_stereo.
Transpiler consolidation (#165)
synth :NAMEidiom —synth :prophet, note: :c4, release: 0.5now dispatches the named synth. Previously every such call silently fell back to:beepbecause the transpiler emittedplay(opts)instead ofplay(note, opts).- Top-level
use_synthordering —use_synth :saw; play :c4; use_synth :tri; play :e4now plays the right synth for each note. Previously theuse_synthcalls were hoisted together and only the last one took effect. - Single transpiler path — the legacy regex-based transpiler has been removed. TreeSitter is now the sole Ruby → JS path, eliminating a class of inconsistencies where one transpiler handled a construct and the other didn't.
- Ruby array helpers:
.zipand.each_with_indexnow transpile correctly.
Bug fixes
get[:key]bracket form —if get[:flag] ... endbranches now read the stored value. Previously the bracket form silently returnedundefinedfor every key, so any composition that gated loops on shared state (set/get) appeared inert — no error, no warning, just nothing happening.get(:key)function-call form was unaffected.synth :sound_inconnect error — every dispatch used to printMic input failed: Failed to execute 'connect' on 'AudioNode': Overload resolution failedto the log. Fixed in initial review.synth :sound_inmic lifecycle — three sharp edges fixed during review testing:- Stop now releases the mic track so the browser's recording indicator clears and nothing keeps feeding scsynth's input channel.
- Rapid re-dispatch inside a
live_loop(e.g.,synth :sound_in; sleep 0.1) no longer re-acquires the microphone ten times per second. The mic connects once and stays connected. - Hot-swap (edit code to remove
sound_in, press Run again) cleanly releases the mic without needing an explicit Stop first.
- MIDI dual-fire drop — the new
/midi:*:ch/type+/midi/typedual dispatch was silently dropping every event because the scheduler'sfireCueearly-returned when called with a synthetic taskId. MIDI input now actually wakes sync waiters.
Verified compatibility
The 56 real-world compositions from the beta.0 regression set still pass end-to-end. New automated E2E coverage (tests/p2-credibility.spec.ts) exercises the four new features in headed Chromium with mocked MIDI input and a mocked microphone stream.
Known gaps (follow-up)
use_real_timelatency reduction is wired through the scheduler but hasn't been measured against real MIDI hardware — perceptual verification is still owed.synth :sound_inhas been verified to reach scsynth end-to-end and real mic passthrough works in manual testing, but an automated fidelity test (known tone in → known tone out with FFT verification) is still owed.
Bug reports welcome on GitHub issues. The in-app Report Bug button pre-fills the version, browser, and current code.
v1.5.0-beta.0
Prerelease. This is the first public beta of v1.5.0. Installed via npm install @mjayb/sonicpijs@beta; the default latest tag still points to v1.4.0. Bug reports welcome on GitHub issues — the in-app Report Bug button pre-fills the version, browser, and current code.
Engine audit — 33 bugs fixed
Found during a multi-round audit against MagPi book chapters, official Sonic Pi wizard/sorcerer/magician examples, and community forum compositions. Per-bug rationale in the engine audit PR (#155) and synth alias cleanup (#157).
Critical engine:
randomSuffix()wasconst f = (): string => f()— infinite recursion on top-levelin_thread/atProgramBuilderchildren (in_thread/with_fx/at) not inheriting_transpose,_synthDefaults,_sampleDefaultswrapBareCodedidn't trackloop do/.times do/.each do—use_synthhoisted out of loopsnormalizeSampleParamsmissingexpandSlideParam—sample :x, slide:silently ignorednormalizeFxParamsresolved symbols AFTER injecting defaults (wrong order)with_bpmdidn't restore previous BPMresumeTick()race condition on rapid pause/resume
Bridge / runtime:
in_threadinsidewith_fxnot inheritingoutBus— thread audio bypassed FX entirely- Sample/SynthDef lazy-load race conditions (buffer leaks, duplicate loads)
freeBus()allowed duplicate frees — bus collisionfetchSampleDurationsilently swallowed errors, breakingbeat_stretch
DSL / tutorial parity:
use_sample_bpm— wrapper arounduse_bpm(60 / sample_duration(name))midishorthand —midi 60, sustain: 0.5(note_on + auto note_off)use_osc/oscshorthand — state variable for default host/portwith_fx reps:— FX body now loops N times within same FX instancewith_synth_defaults/with_sample_defaults— scoped variantsuse_density— permanent density setteruse_debug— exposed in sandbox as no-op (was causing crashes).take(n)— transpiler was emitting.slice()but Ring uses.take()Ring.line(steps=1)returned[NaN](division by zero) — now returns[start]
Public API additions:
- Exported:
hzToMidi,chord_degree,degree,knit,range,line
Sandbox fixes:
get()was defined as anew Proxy(...)not a function —get(:key)crashedwith_random_seed,with_octave,with_density,with_synth_defaults,with_sample_defaultsweren't intranspileWithBlockhandler — blocks transpiled without callback wrapperTOP_LEVEL_SETTINGSmissinguse_arg_bpm_scaling
Architectural fix — b shadowing:
- User variable
bshadowedProgramBuildercallback parameter via Proxysettrap. Renamed internal parameter frombto__bacross all transpiler output sites (callback params, method prefixes,__checkBudget,stop,lastRef,density,definefuncs,atcallbacks). Documented in KNOWN_LIMITATIONS.
wrapBareCode regex expansion:
- Regex only matched
/^\s*(play|sleep|sample)\s/— missedplay_chord,play_pattern_timed,use_synth_defaults,synth,control,cue,sync, etc. Expanded to cover all bare DSL callables that need__bscope.
Synth aliases:
- Removed
dark_sea_hornandsingerfromSYNTH_NAMES— no corresponding.scsyndefon the SuperSonic CDN (silent failure trap).
Verified compatibility
56 real-world compositions run end-to-end in Chromium capture:
- 18 E2E tests (covering 122 DSL functions)
- 7 MagPi Essentials chapter examples
- 15 official Sonic Pi examples (wizard/sorcerer/magician tiers from
etc/examples/) - 13 community forum compositions from in-thread.sonic-pi.net
- 3 edge cases
Test coverage
- 703 unit tests pass
- Zero TypeScript errors
UI
- Version label in the menu bar displays
v1.5.0-beta.0(top-right, muted). Click to copy the fullSonicPi.js v1.5.0-beta.0string to clipboard. Pre-filled into the Report Bug URL so every bug report is tagged to a specific build.
Release engineering
- This is the first release following the new release-cycle criteria: single version string across all surfaces, npm
--tag betato protectlatest, UI footer as the load-bearing signal for the prerelease channel, explicit beta → RC → stable gate criteria documented in the release runbook.
Known limitations
See KNOWN_LIMITATIONS.md for the current list. The limitations file is the single source of truth; it is intentionally NOT duplicated per-release in this changelog. Noteworthy feasible-but-not-built items tracked for post-beta work: external sample upload (#159), OSC receive via WebSocket bridge (#150), synth :sound_in via getUserMedia (#152), use_real_time MIDI latency bypass (#149), .zip/.each_with_index Array methods (#154).
v1.4.0 — UI/UX overhaul + error handling
What's new
Help Panel
- Fixed cursor word detection (CodeMirror ESM
wordAt()bug) - Resizable with draggable splitter (height persists)
- 311 help entries — 33 functions + 37 synths + 34 FX + 207 samples
View Menu
- All 5 panel toggles now work: scope, log, cue log, buttons, tabs
- Scope visualization stays within its panel (overflow fixed)
Cue Log
- Wired to live cue/sync events from the scheduler
- Resizable with splitter between log and cue log
Error Handling
- Syntax errors block execution with clear "your code could not be parsed" message
live_loop,define,with_fx,in_thread,at,time_warp,densityall validatedo...endblocks- Runtime errors now highlight the error line in the editor
- Hot-swap
reEvaluate()rolls back on failure
UI Polish
- Autocomplete tooltip dark-themed
- Report Bug button (pre-filled GitHub issue, no OAuth)
- New hero image
Infrastructure
- CI workflow on PRs
- GitHub Actions v6, TypeScript 6.0.2
- Dependabot configured
- Blade Runner x Techno in advanced examples
Milestone
5 issues closed, 4 PRs merged
Full Changelog: v1.3.0...v1.4.0