Skip to content

websim: extend the web simulator to all logue-SDK platforms - #212

Open
wilvk wants to merge 6 commits into
korginc:mainfrom
wilvk:platform-websim-playback
Open

websim: extend the web simulator to all logue-SDK platforms#212
wilvk wants to merge 6 commits into
korginc:mainfrom
wilvk:platform-websim-playback

Conversation

@wilvk

@wilvk wilvk commented Aug 9, 2026

Copy link
Copy Markdown

Summary

Extends the existing web simulator so that every logue-SDK platform can build its DSP to
WebAssembly and run in the browser, not just NTS-1 mkII and NTS-3.

Today websim/ supports the two gen-2 NTS platforms via a wasm: recipe duplicated inline in
each project Makefile. This PR factors that recipe into a shared build fragment and adds the
host-side bridges needed by the other platforms, so the same unit.cc that ships to hardware
also runs in a browser tab for microKORG2, drumlogue, and the gen-1 targets
(prologue / minilogue xd / NTS-1 mkI).

Nothing about the hardware build changes: no linker scripts, no CMSIS, no _unit_base.c, and
no ARM build flags are touched. The wasm path is additive.

What's here

Area Change
websim/wasm.mk Shared wasm / wasm-build / render targets, extracted from the per-project recipe that was duplicated in every NTS project Makefile
websim/legacy.mk The same entry point for gen-1 units, whose DSP is free C functions emitting q31 rather than a C++ class
websim/dsp/** Host stand-ins for the firmware ROM, plus a bridge per platform family (microKORG2, drumlogue, gen-1)
platform/** A small wasm.cc shim and a wasm target for each unit
Makefile (new, repo root) make websim PROJECT=… to build and open one unit; make list, make clean, make setup
WEBSIM.md Full guide: prerequisites, per-platform setup, how the bridges work, troubleshooting

Existing NTS projects

The 8 NTS-1 mkII / NTS-3 project Makefiles lose their inline wasm: block in favour of

WEBSIM_SHELL    := osc.html
WEBSIM_INCFLAGS := $(INCDIR)
include $(SANDBOXDIR)/wasm.mk

Same emcc invocation, same output — one copy instead of eight.

Bug fixes in platform/microkorg2/common/utils/

Building microKORG2 units without NEON exercised the #else branches of the SIMD helpers,
which turn out not to compile as written. These are fixed here because the wasm build needs
them, but they are latent bugs in the non-NEON path regardless of websim:

  • int_simd.h — six *_str macros are missing a statement terminator:
    do { *(int32x4_t *)(ptr) = (v) } while (0);. Any use is a syntax error.
    Affects s32x4_str, s32x2x2_str, s32x4x2_str and the u32 equivalents.
  • int_simd.hconst int2x2_t tmp = … in the non-NEON clip helper names a type that
    does not exist; it should be int32x2_t.
  • float_simd.h — the non-NEON vtrn / vzip / vuzp helpers declare
    const float32x2x2_t v; and then assign to v.val[0], which is an assignment to a const
    object. Also switches the {{…}} aggregate initialisers to the vector constructors.

These are worth reviewing on their own merits — happy to split them into a separate PR if you
would prefer to take them independently of the websim work.

The other two files in that directory are guarded so the ARM build is byte-identical:

  • cortexa7_intrinsics.h — the Cortex-A7 DSP ops are inline ARM assembly that clang cannot
    assemble for wasm. Portable equivalents with matching saturating semantics are provided under
    #ifdef __EMSCRIPTEN__. One caveat: SEL depends on the APSR.GE flags set by a preceding
    SIMD instruction, which cannot be reproduced standalone, so the shim returns its first
    operand — the same compromise as the existing gen-1 __SEL shim.
  • mk2_utils.h__builtin_prefetch requires constant arguments under clang/wasm, so
    PrefetchMemory becomes a no-op under #ifdef __EMSCRIPTEN__.

Example units

Three new units, included because they exercise build paths the existing templates don't:

  • platform/drumlogue/sample-voice (SmplVox) — a sample-playback drum voice; the first
    drumlogue unit that uses the sample bank API.
  • platform/nutekt-digital/twosaw — a gen-1 oscillator that derives its websim parameter
    table from manifest.json rather than a hand-written macro.
  • platform/nutekt-digital/tremolo — a gen-1 modfx example.

Happy to drop these from this PR if you'd rather keep it to the build system alone.

Verification

Both gates run from a clean checkout with a pinned Emscripten (4.0.16):

$ make build-all
build-all: 28 project(s) built OK

$ make render-all
render-all: all renders finite and OK

build-all compiles and links every wasm-capable unit across all six platforms.
render-all builds each unit in a headless render mode (no AudioWorklet), runs it under node,
and asserts the output WAV is finite — catching crashes and numerical blow-ups without a
browser. Both are wired into .github/workflows/websim.yml.

Browser behaviour was checked by hand per unit: page loads cross-origin-isolated, the keyboard
drives pitch for oscillators, input plays through for effects, and every parameter slider
audibly changes the sound.

On the CI workflow: if you would rather not take a GitHub Actions workflow from an outside
contributor, say so and I'll drop .github/workflows/websim.yml — the build-all and
render-all targets stand on their own and can be run manually or wired up however you prefer.

Notes for review

  • Emscripten 3.1.x or newer is required (SIMDe arm_neon.h for drumlogue, and recent
    enough to reject -I …/emscripten/system/include). The workflow pins 4.0.16.
  • The NEON-heavy microKORG2 units (vox, MorphEQ, MultitapDelay, Vibrato, breveR)
    build through a struct-shape SIMD shim rather than SIMDe; see WEBSIM.md.
  • Audio is not bit-exact against the ARM build — the wasm path scalarises NEON, so the
    render checks are tolerance-based rather than golden-file comparisons.
  • microKORG2 oscillators are single-voice by default; vox opts into 4-voice polyphony via
    MK2_OSC_VOICES.

Willem van Ketwich and others added 6 commits August 9, 2026 19:00
Adds the compile path that lets every logue-SDK platform build its DSP to
WebAssembly:

  - websim/wasm.mk    shared `wasm` / `wasm-build` / `render` targets for gen-2
  - websim/legacy.mk  the same for gen-1 (nutekt-digital, minilogue-xd, prologue)
  - websim/dsp/**     firmware-ROM stand-ins and per-platform bridges
                      (microKORG2 incl. NEON/SIMD compat, drumlogue, gen-1)
  - platform/**       a wasm.cc shim + wasm target for each unit

The NTS-1 mkII / NTS-3 projects move from a duplicated inline `wasm:` recipe
to including the shared fragment. microKORG2's SIMD utility headers gain
non-NEON fallbacks so the same sources compile under Emscripten.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A synth unit built around drumlogue-specific capabilities: on-board PCM
sample-bank access via the runtime descriptor (get_num_sample_banks /
get_num_samples_for_bank / get_sample), the Linux/Cortex-A7 C++ runtime,
and gate-based drum triggering with per-hit velocity.

Browses sample banks live (showing real on-device sample names), resamples
the selected sample with linear interpolation, and shapes it with a decay
envelope, pitch-sweep envelope, bit-depth reduction, drive, and pan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two nutekt-digital (gen-1) units that exercise the legacy build path both on
hardware and through websim/legacy.mk. twosaw derives its websim parameter
table from manifest.json via gen_legacy_params.py rather than a hand-written
macro.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reworks the three shell pages so a unit compiled by wasm.mk/legacy.mk can be
driven from the browser: a playback toggle that survives a reload, master
volume, a QWERTY keyboard with octave switching and latch, parameter sliders
built from the unit's own descriptor, and time/frequency scopes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
  make websim PROJECT=<path|name>   build one unit and open it via emrun
  make list / clean / setup         discover projects, clean, bootstrap emsdk
  make render / render-all          headless WAV render + finite-output check
  make build-all                    compile every wasm-capable unit (CI gate)

WEBSIM.md documents the flow end to end; the GitHub workflow runs build-all
and render-all so the wasm build and audio output stay green without a browser.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The comments cited WEBSIM_FOLLOWUP_PLAN.md / WEBSIM_EXPANSION_PLAN.md, which
are working notes that live outside the SDK tree. Comment text only; no code
or build changes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@radfaraf

radfaraf commented Aug 9, 2026

Copy link
Copy Markdown

This is an incredible contribution. Hopefully the KORG team can find the time to test and approve this as it would greatly increase the value of their products.

@xiashj24

Copy link
Copy Markdown
Contributor

Thank you for putting this together. I will take some time to review it.

@xiashj24
xiashj24 self-requested a review August 12, 2026 01:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants