/ proof-of-concept / cymmodes
Sign in
proof-of-concept / cymmodes
cymmodes / README.md
51 lines · 8.9 KBCodeBlameHistory
3A browser companion of [cymsim](../cymsim) for studying how the linear modes of a cymbal couple. The motivating question is the upward cascade: a struck cymbal receives most of its energy in a few low modes, and the geometric nonlinearity of the shell carries that energy into hundreds of higher modes, which is the crash. Time-domain simulation shows the cascade in the radiated sound; what it does not show directly is which mode fed which. Here we compute the linear modes of the shell, drive a chosen one in the full nonlinear model, and watch the energy of every mode over time.
5The pieces are taken from the two sibling apps and joined by a projection layer. The linear modes come from the eigensolve pipeline of [cymplay](../cymplay): the tangent stiffness of the Marguerre-von Karman shell, statically condensed onto the transverse DOFs, mass-normalised eigenmodes by Householder plus implicit QL, all in TypeScript in a Web Worker, with the same modes file format (a file saved here loads in cymplay and vice versa). The nonlinear run is the time loop of [cymsimweb](../cymsimweb): the same five-DOF discretization as the Python package, explicit leapfrog on the GPU through WebGPU (two compute dispatches per sub-step), with a float64 CPU fallback. New in this app are the drive (a modal initial condition or a distributed harmonic force), a projection pass that contracts the running state with the mass-weighted mode shapes, and the views that display the result per mode.
7## Running
9 npm install
10 npm run dev # development server
11 npm run build # static site in dist/
12 npm test # CPU physics tests, then the GPU kernels under Dawn
14Requires Node 20 or later and, for the GPU time loop, a browser with WebGPU (Chrome or Edge 113+). Without WebGPU the run falls back to the CPU loop, which is fine at 15 mm and slow at 10 mm.
16## Using the app
18**1. The cymbal.** Start from a preset (the cymsim examples, plus a symmetric cymbal with the hammering removed, on which the mode pairs stay nearly degenerate and the internal resonances are easiest to read) or load a cymsim YAML. The design panel is cymplay's: material and damping are *live* (they rescale the frequencies analytically), the geometry and the imperfections are *structural* and mark the modes stale.
20**2. The modes.** Compute the eigenmodes at the chosen mesh size, once per shape, in the browser; they are cached in IndexedDB and can be saved and reloaded. The eigensolve is dense and costs about the cube of the node count: 15 mm takes seconds, 10 mm (the default, 1736 nodes and about 1500 modes below 20 kHz) about twenty seconds, 6 mm about a quarter of an hour. The mode table lists frequency, T60 and a heuristic (m, n) label (nodal diameters and circles, read off the discrete shape; on a hammered cymbal this is a description, not a mode number). Clicking a mode shows its shape in the 3D view, animated slowly at the amplitude set below.
22**3. Drive a mode.** Two mechanisms:
24- *Free decay.* The shell is released from the mode shape at a chosen amplitude, given as the peak displacement at the antinode in units of the local thickness, and rings down. The in-plane displacements and rotations of the initial state come from the static condensation (one banded solve per mode, cached), so the released state is the complete linearised mode rather than a pure-w field that would also launch fast membrane and rotation transients. Below about 0.1 thicknesses the decay is linear; around one thickness energy starts leaking into specific partners; at several thicknesses the cascade is broadband.
25- *Harmonic forcing.* A force distributed as the mode shape and weighted by the nodal mass, so it is orthogonal to every other mode: in linear theory only the driven mode responds, and whatever else appears in the nonlinear run got there through the coupling. The force ramps up over a set time, holds at the drive frequency (detunable in cents, since the nonlinear resonance shifts with amplitude), and stops after the set drive time so the tail is a free decay. The amplitude is specified as the linear steady-state response at physical damping; scaling the damping down makes the true response larger, which the log states.
27The damping scale (0 to 1) applies to both loss terms of the run only; 0 gives a conservative shell, on which energy transfer is not obscured by decay. The *linear* checkbox runs the linearised model instead, as a control: any energy appearing in other modes there is discretization leakage (the simulator carries in-plane and rotational inertia that the condensed eigenproblem drops), typically well below the nonlinear transfer.
29**The views.** During the run the plate shows the live displacement. The analysis panel offers three views on one frequency axis. *Modal energy* is time against mode frequency, one horizontal trace per mode, colour the modal energy in dB below the loudest value seen: energy arriving in a mode lights its trace up, resolved per mode rather than smeared by an STFT window. The two *spectrogram* views are true STFTs, of the listener signal and of the driven mode's own coordinate (captured at the full audio rate on the GPU), with the linear mode frequencies overlaid as ticks. The distinction matters: the modal view says which shapes hold the energy, pinned at their linear frequencies, while the spectrograms say at which frequencies the motion actually oscillates. Combination tones between the modes, the amplitude-dependent shift of the driven peak off its dashed linear frequency, and the broadband floor of the turbulent regime appear only in the spectrograms; which mode carries them appears only in the modal view. Hovering a spectrogram names the nearest linear mode and the offset in hertz. The bar view shows every mode at the cursor (or the frontier of the run) and names the strongest ones; clicking a trace or a bar selects that mode, so one can walk the cascade asking of each excited mode what its frequency is and whether it is a sum or difference of others. The energy readout compares the modal sum with the shell's total energy; at strong amplitudes the modal sum falls well short of the total, because the in-plane motion and the quadratic part of the membrane energy are not in the linear basis. That gap is itself a rough meter of how nonlinear the state is.
31**4. The sound.** The run also evaluates the Rayleigh-integral listener of cymsim (front and diffracted-back terms), playable and exportable as WAV.
33## The projection
35With mass-normalised transverse shapes phi_m and the simulator's transverse lumped mass M, the modal coordinate is q_m = phi_m^T M w and the modal energy E_m = (qdot_m^2 + omega_m^2 q_m^2) / 2. The state is projected on the GPU (one workgroup per mode, at a selectable snapshot rate, 240 per second by default), so tracking 1500 modes adds a negligible cost to the run. Note that the basis is the *condensed* eigenproblem while the simulator carries all five DOFs per node, with an inflated rotational inertia as a time-step device; the frequencies therefore differ by a few percent at the top of the band, and a linear run leaks a little energy between close modes. The tests quantify this: on the coarse test cymbal a linear release keeps more than 85 % of the energy in its mode over the run, while a strong nonlinear release moves out more than five times as much.
37## Layout
39| path | contents |
40|---|---|
41| `src/config/`, `src/model/` | configuration and shell model, shared with cymsimweb (schema, mesher, DKT element, shell assembly, exciters, radiation, set-up) |
42| `src/precompute/` | eigensolve pipeline shared with cymplay, plus the condensation-complement operator and the extended worker |
43| `src/modes.ts`, `src/cache.ts`, `src/derived.ts`, `src/design.ts` | modes file format, IndexedDB cache, live material relations, design panel (shared with cymplay) |
44| `src/cpu/`, `src/gpu/` | the time loops of cymsimweb, extended with the initial state, the distributed drive and the projection pass |
45| `src/drive.ts`, `src/modal/` | modal initial conditions and forces, the energy tracker, the (m, n) classifier |
46| `src/ui/`, `src/main.ts` | plate view (cymsimweb), mode table, modal spectrogram and bar views, wiring |
47| `tests/` | node:test physics checks (complement, projection, linear purity and decay, nonlinear transfer, forcing) and the Dawn GPU check |
49## Tests
51`npm run test:cpu` checks, on a small cymbal: that the condensation complement makes a mode shape an equilibrium of the in-plane and rotation DOFs with the transverse force equal to -omega^2 M w; that the projection of the initial state is the driven mode alone; that a small linear release stays in its mode and decays at exp(-2 sigma0 t); that a strong nonlinear release moves energy into other modes while the linear control does not; and that resonant forcing excites only the driven mode, with the audio-rate trace agreeing with the snapshot projection where they coincide. `npm run test:gpu` runs the same WGSL kernels under Node with Google Dawn and compares the modal snapshots, the trace and the energies with the CPU loop.
↑↓ move↵openescclose