/ simulated_instruments / cymbal
Sign in
simulated_instruments / cymbal
cymbal / webapp / README.md
54 lines · 6.3 KBCodeBlameHistory
3Interactive browser implementation of the physical model described in `../README.md`. The algorithm itself is written in MATLAB syntax, in `../matlab/*.m`, and executed client-side by [numbl](https://numbl.org) inside a Web Worker; the TypeScript here is only the UI, the plots, the WAV encoder, and the marshalling of parameters and tensors in and out of the numbl runtime.
7da0671Hammer the cymbal: hybridize near-degenerate modes, add the turbulent fluctuation about the mean cascade, and flatten the decay across the bandJeremy Magland 5The signal chain: a Hertz stick contact (or a seeded repeated figure, with `Strokes` above 1), the modes of a bronze shell of revolution from one finite element eigensolve per nodal-diameter order, hybridized to stand for the hammering that makes a real cymbal irregular, a nonlinear cascade of modal energy up the frequency ladder with the turbulent fluctuation about it, an oscillator bank radiating through a coherent sum of surface dipoles to the listener point, and Kaiser-windowed anti-aliased decimation to 48 kHz.
7## Usage
9```
10npm install
11npm run dev # development server
12npm run build # production build in dist/
13npm run selftest # run ../matlab/cymbal_validate.m through numbl in Node
14```
16The self-test runs the closed-form and reproducibility checks and the pinned diagnostics. The MATLAB code can also be run directly: `cd ../matlab && numbl run cymbal_demo.m` (or `cymbal_validate.m`).
18## Where the time goes
20The expensive stage is the mode basis: one generalized eigensolve per nodal-diameter order, about two seconds at the defaults, and that is exactly the piece the worker caches. Striking somewhere else, changing the stick, moving the microphone or retuning the cascade all reuse the cached modes. The synthesis that follows is an oscillator bank rather than the closed-form exponentials of the sibling `triangle` repository, because the cascade makes every amplitude time-varying; the per-frame phase advance is a unit-modulus multiply and the oscillator basis is hoisted out of the loop, so a three-second render costs about the same again.
22## Reading and editing the code in the app
24The Code button opens the MATLAB source in a CodeMirror editor with MATLAB syntax highlighting. Every `.m` file in `../matlab` is listed, tagged by whether the app executes it or it is a CLI entry point. The sources are editable: an edit re-renders after the usual debounce, so changing the physics in the editor is heard and plotted the same way moving a slider is. Zeroing the meridian curvature `cs` in `cymbal_shell.m`, for instance, decouples bending from stretching and collapses the partials back onto those of a flat disc.
26Edits live in memory only. They are deliberately not persisted, so a reload restores the code the app shipped with; "Revert file" and "Revert all" undo them sooner, and a dot marks the files that differ from the shipped source. A fault in edited code is reported with its file and line (numbl skips a file it cannot parse and then fails at the call site, so the worker captures the parse warning and reports that instead), the last successful render stays on screen, and reverting recovers.
28## How rendering is tiered
30Changing any parameter triggers a debounced fast preview (oversample 1); the Render button runs the full-quality pass. While the current result is a preview, the Play and Download buttons and the status badge say so. The worker runs each stage as a separate numbl execution and keys its caches by tier: plate geometry, material, damping, radiation scaling and discretization parameters invalidate the mode basis; the stick, the gesture, the cascade and the listener only re-run the strike process, the cascade and the radiation; pre-delay, normalization and bit depth only re-run the output stage. Intermediate tensors stay inside the worker as numbl RuntimeValues.
32The New seed button redraws the timing and velocities of a repeated figure without touching any other parameter (a single stroke is deterministic, so the button is disabled there). The Loop button repeats the render continuously, swapping in each new render as parameters change. Parameters persist in localStorage; "Reset to defaults" restores the values in `src/params.ts` (which `../matlab/cymbal_defaults.m` mirrors).
34## Layout
36- `../matlab/` — the algorithm: `cymbal_profile.m`, `cymbal_shell.m`, `cymbal_basis.m`, `cymbal_at.m`, `cymbal_point.m`, `cymbal_strikes.m`, `cymbal_rng.m`, `cymbal_cascade.m`, `cymbal_radiate.m`, `cymbal_output.m`, plus `cymbal_render.m` / `cymbal_demo.m` / `cymbal_validate.m` for CLI use
37- `src/params.ts` — parameter schema, defaults, tier assignment, persistence
38- `src/mfiles.ts` — the `.m` sources, bundled at build time and hashed for cache keying
39- `src/worker/renderWorker.ts` — off-thread numbl execution with tiered caching
40- `src/lib/profile.ts` — the cymbal meridian, mirrored from `cymbal_profile.m` for the scene
41- `src/components/` — 3D scene (three.js), parameter panel, code panel and editor, plots (strokes, cascade, waveform, spectrum, spectrogram, mode map, mode shapes)
42- `src/selftest.ts` — Node harness for the MATLAB validation script
44## The cascade plot
46The plot the sibling repositories do not have. The cascade solve tracks the energy of every mode at the control rate, and the plot collapses that onto a log-frequency by time grid, so the upward flux is visible directly. Dropping `Cascade gain` to zero flattens it into horizontal streaks, every mode simply decaying where the stick left it, and the sound turns from a crash into a gong.
50`Hybridization` at 0 is the ideal, perfectly round plate. Its high nodal-diameter modes cannot radiate at all, so a few low-order partials carry everything and the instrument becomes a small bell; sliding it back up is hammering the cymbal. `Fluctuation` at 0 leaves every mode with a smooth envelope, which is a few hundred clean partials, that is to say a chime; up at 0.9 each partial broadens into a band and they close into a wash. `Stroke velocity` sweeps the whole dynamic behaviour through one physical quantity, because the cascade strength goes as the square of the amplitude: at 0.7 m/s the same cymbal pings, at 6 m/s it opens right up.
54The stroke pattern draws from a 32-bit LCG in `cymbal_rng.m` rather than MATLAB's `rand`, so a given seed produces the identical pattern in the numbl CLI, in Node, and in the browser. That is what lets the self-test pin exact impulse sums and stroke times.
↑↓ move↵openescclose