Angklung Simulator
A browser angklung that shares a song across a room of players, every note computed from bamboo physics

- audio files, every note synthesised
- 0
- playing techniques from one bamboo model
- 3
- cited tunings, each with its caveat
- 3
- bundled melodies, public-domain or my own
- 5
On this page
The problem
Every angklung app I found online is a soundboard. You tap a picture of bamboo and hear a note. That copies the sound and misses the instrument. In an angklung ensemble, each player holds one or two notes. They spend most of a song waiting, watching a conductor signal numbers by hand. The coordination is the instrument, and nothing on the web lets you try it.
Two smaller gaps followed. The three playing techniques are usually taught as three sounds: kurulung (shake), centok (sharp pull) and tengkep (shake with one tube held). They are three ways of setting the same bamboo ringing. And on an accompaniment angklung, tengkep changes the chord. Four tubes sound a dominant seventh. Hold one with your little finger and it becomes a major triad. No simulator showed that.
What I built
Every note is computed from a model of a bamboo tube
I modelled each tube as a set of resonances that ring and fade, the way struck bamboo does. The site ships no audio files at all, and the build fails if one appears. A tube is closed at one end, so it has almost nothing at twice its main pitch. That gap makes the chord switch provable: the octave tube is the only source of that pitch, so holding it removes the note completely. The three techniques differ only in how the tube is struck. Centok is one hard hit, kurulung is a steady shake, and tengkep is the same shake with some tubes silenced.
The sound is measured by tests
I kept the sound engine free of any browser code, so it can render audio inside the test suite. Tests measure the pitch to within a few cents. They check that the held tube's note is gone under tengkep and present without it, and that centok makes exactly one strike. What you hear in the browser is the same audio the tests measured. Notes are timed against the audio clock itself, so a long piece never drifts.
The ensemble view shows who plays when
The main screen loads a melody, a set of angklung and a number of players. It shows who holds which notes and when each person comes in. Rests are drawn as empty space, because for an angklung player the waiting is the job. You can pick one player and play only that part, or mark players absent and watch the song break. When a song can't be shared out, the solver says why: a note is missing from the set, one player is needed twice at once, or there aren't enough players. It never trims the song to make it fit.
Cultural sources come with their limits
Padaeng is the diatonic-chromatic tuning Daeng Soetigna created in 1938. It ships beside two traditional tunings, salendro and pelog degung, as editable sets of intervals. Each one carries its source and a required note saying where the figure stops being verified. The salendro and pelog numbers are structural models, not measurements, and the build refuses to run if a note is missing. The site names Daeng Soetigna and Udjo Ngalagena on the page, and points to Saung Angklung Udjo and local ensembles.
Result
Angklung Simulator is live and public, in Indonesian and English. It has the rack of swaying angklung, the ensemble view, a technique lab that shows the strikes feeding the bamboo, the chord switch drawn as tubes, a side-by-side tuning comparison and a conductor lane. It also has a diagnostics page that reports how audio behaves on your phone. It runs on three runtime libraries and no audio or sound-processing library. I built it in about 37 hours over 12 and 13 August 2026.
Under the hoodTechnical detail for engineers
- Each mode is a two-pole resonator,
y[n] = g·x[n] + 2r·cos(ω)·y[n-1] − r²·y[n-2], whose impulse response is a decaying sinusoid. The tube behaves as a stopped pipe, so odd partials dominate. - Kurulung is a strike train: a 2–3 Hz shake, two strikes per shake, with seeded jitter in timing and strength. Tengkep uses the same train and changes only which resonators are summed. Adding a technique means adding a pattern, not a branch.
- The synthesis core is
(params) → Float32Array, with no Web Audio, DOM, React, clock or module-level state, enforced bytests/synth/purity.test.ts. The same seed renders byte-identically. FFT and onset-detection helpers are hand-written intests/synth/helpers/dsp.ts. lib/audio/voices.tsplays the renderedFloat32Array; it never rebuilds the model in oscillator nodes. Kurulung and tengkep render at a fixed six seconds and fade on release, an approximation kept at the audio boundary.- The scheduler computes onsets as
startAudioTimeSec + eventTimeSec, never accumulated. Clock and timer are injected so the queue logic is unit-testable in Node. - Infeasibility is a discriminated union:
nada-di-luar-set,nada-bertumpuk-sendiri,pemain-kurang, with the offending note indexes. The solver also reports the piece's peak instant, so the minimum ensemble size is checkable. It is verified against a brute-force oracle (620 lines, 56 tests). - The chord-switch fixture has a 10-cent tolerance that isn't allowed to be relaxed. The technique lab's mode tuner drives the real renderer and emits a
TABUNG_MODESliteral to paste back into the source. TuningSource.caveatis required and enforced bydata:validate. Data: 3 cited tunings, 5 angklung sets, 5 melodies, all validated at build time.- The PRD asked for click for centok and hold for kurulung. Telling a tap from a hold would delay every note, so the rack uses a technique selector plus press-and-hold.
- Warm-up renders one buffer per idle slot so a phone doesn't freeze. The voice budget of 28 came from
pnpm bench:voices, which cleared 64 offline voices at about 18× realtime. - A silent-playback bug had three causes at once: output level, voice budget, and no way to tell them apart. The fix added visible audio errors, a Safari fallback and the
/diagnostikpage. scripts/postbuild.mjsfails the build on any audio file. Runtime dependencies arenext,reactandreact-dom. First Load JS is 108–110 KB per page against a 250 KB budget.- 7 routes × 2 locales = 14 static pages. 132 tests in 11 files, about 13,100 lines of TS/TSX/JSON, 82 commits.
Screenshots
Hiring, or building something similar?
I'm open to full-time roles and select freelance work.