/ proof-of-concept / steelsim41
Sign in
proof-of-concept / steelsim41
steelsim41 / README.md
100 lines · 12.8 KBCodeBlameHistory
8db04e0Design scripts in MATLAB syntax, run by numblJeremy Magland 3A browser application for designing a virtual cymbal and simulating a drumstick strike on it. The cymbal is described by a short script in MATLAB syntax (material, bounding box, outline, thickness field, stress-free profile, mount, and losses), run in the page by [numbl](https://numbl.org), that the user edits in the page or that an AI writes for them. The sound is computed by solving the geometrically nonlinear (von Karman) plate equations with in-plane inertia and a spatially varying thickness, stepped explicitly on the GPU through WebGPU. The model and the numerical method are described in [THEORY.md](THEORY.md).
5The physics and most of the interface come from steelsim31 (the hanging steel sheet); the changes here are the scripted design, the thickness field, the mount, an arbitrary outline (including a center hole), and the removal of the push excitation, the hit series, and the feature-list shape editor.
7The app is deployed by the `deploy` workflow on every push to `main`: https://steelsim41--proof-of-concept.vault1-sites.magland.org/
9## Running
11Requires Node 20 or later and a browser with WebGPU (Chrome or Edge 113+; Firefox and Safari with WebGPU enabled).
13 npm install
14 npm run dev # development server
15 npm run build # static site in dist/
16 npm test # all tests (see below)
18## Using the app
6201bf2Show the mount and the listener in the 3D view; lay the plate flatJeremy Magland 20The sidebar holds the controls at its top, then the design script and the strike and simulation parameters. The main area shows the plate (a lit 3D height field by default, or a 2D color map; drag to orbit, wheel to zoom), the movie and playback toolbar, and the spectrogram of the listener signal. The 3D view lays the plate flat, with its normal pointing up, the way a cymbal sits on a stand, and opens looking down on it from about thirty degrees.
22The head of the sidebar, with the recorded duration and the Start, Pause, Stop, and Continue buttons, does not scroll, so the controls stay in reach however long the script is. The bar between the sidebar and the view can be dragged to widen the panel (double-click to return to 420 px); the width is remembered in the browser. The panel shows controls and measured quantities only: the explanatory prose is folded away under About, Writing a script, and the two Notes headings, which can be opened when it is wanted.
6edbaa3Add a dented 16 inch crash presetJeremy Magland 24**Design script.** Pick a preset from the dropdown (a flat steel rectangle by default; a bowed rectangle, a flat disc on a stand, a 16 inch crash, the same crash with a dozen dents, a 20 inch ride, an ellipse with a twist, an 18 inch china) and edit it freely; the script is compiled as you type (or with Ctrl+Enter) and the view shows the resulting stress-free shape, colored by height or by thickness. Errors are reported under the script, and a summary lists the bounding box, the material, the thickness range, the mass, the grid, the profile's peak-to-peak height and largest slope (with a warning above the shallow-shell range), the mount, and the losses. Revert restores the selected preset.
7710254Self-contained design scripts; 3D by default; resizable, less verbose panelJeremy Magland 26The script is written in MATLAB syntax and run by [numbl](https://numbl.org). It fills in a struct named `design`. All lengths are meters. The outline, the thickness, and the profile are numbers or function handles `@(x, y, r, theta)` of the point relative to the center of the bounding box, its distance from the center, and its angle from the x axis; the handles are applied to whole grid matrices, so they use elementwise operators.
28A script is self-contained. Nothing is in scope but numbl's own functions, so a script defines the shapes it needs as ordinary MATLAB functions at the bottom of the file, as it would in a `.m` script, and the unit constants at the top. Nothing is hidden behind a helper library that a reader would have to look up or an author would have to remember, and a script can be copied out of the page and run elsewhere unchanged. The trade-off is that the presets repeat a few small functions between them.
30```matlab
32D = 16*inch; R = D/2; hole = 12*mm;
8db04e0Design scripts in MATLAB syntax, run by numblJeremy Magland 33bellR = 60*mm; bellH = 12*mm; bowH = 16*mm;
7710254Self-contained design scripts; 3D by default; resizable, less verbose panelJeremy Magland 34tBell = 1.4*mm; tEdge = 0.8*mm;
8db04e0Design scripts in MATLAB syntax, run by numblJeremy Magland 35design.name = '16 inch crash';
36design.material.E = 110e9; % Pa
37design.material.nu = 0.34;
38design.material.rho = 8800; % kg/m^3
39design.bounds.width = D; % bounding box [m]
40design.bounds.height = D;
7710254Self-contained design scripts; 3D by default; resizable, less verbose panelJeremy Magland 41design.inside = @(x, y, r, theta) (r <= R) & (r >= hole/2); % omit for the whole box
42design.thickness = @(x, y, r, theta) tBell + (tEdge - tBell) * ramp(bellR, R, r);
8db04e0Design scripts in MATLAB syntax, run by numblJeremy Magland 43design.profile = @(x, y, r, theta) bowH * (1 - (r/R).^2) + dome(r, bellR, bellH); % stress-free height [m]
44design.mount.radius = 20*mm; % or design.mount = [] for a free plate
45design.mount.stiffness = 2e4; % N/m
46design.mount.damping = 5; % N s/m
47design.loss.sigma0 = 0.6; design.loss.sigma1 = 2.5e-4; design.loss.sigmaIn = 20;
49function v = ramp(a, b, x)
50% 0 at x = a, 1 at x = b, clamped to that range outside.
51v = min(max((x - a) ./ (b - a), 0), 1);
52end
54function v = dome(d, radius, height)
55% A cosine dome of the given height and base radius, with zero slope both at
56% its rim and at its apex; d is the distance from its center.
57v = 0.5 * height * (1 + cos(pi * d / radius)) .* (d < radius);
58end
7710254Self-contained design scripts; 3D by default; resizable, less verbose panelJeremy Magland 61Note that the functions may follow the statements that use them, as they do in MATLAB, and that a handle created above them may call them: numbl hoists the definitions, and the simulator appends its sampling code after the whole script. A function defined in the script also shadows one of the same name defined anywhere else. `inside`, `mount`, `profile`, and `loss` are optional (`mount` may also give `x` and `y`). The profile's mean plane is removed before use, since only its slopes enter the physics and a tilt would add a spurious membrane coupling.
6201bf2Show the mount and the listener in the 3D view; lay the plate flatJeremy Magland 63**Markers.** Both views paint two rings on the plate: the mount, dark, at its radius, and the strike point, yellow, just outside the tip. The 3D view adds two markers of its own. A short post stands at the mount, since on a steep bell the ring alone is easily lost and, seen edge-on, invisible; it shows where and how wide the mount is, and its length means nothing. A microphone stands on the line from the center of the plate to the listener, grille facing the plate.
65The microphone is drawn nearer than the listener is. A listener a meter from a 16 inch cymbal is four times as far away as the cymbal is wide, so no framing holds both at true scale, and the plate already fills most of the view. The drawn distance therefore follows the true one through a saturating map that keeps the microphone in the thin annulus around the plate, and the microphone shrinks a little as the listener recedes. The direction is faithful. The distance is the field in the Listener group, and it is that field that sets the 1/r spreading and the spread of the propagation delays, not the drawing.
67**Strike and simulation.** The strike point is given as fractions of the bounding box (the yellow ring in the view), with the stick speed, its effective mass, the Hertz contact stiffness and exponent, and the tip radius. The default is a hard hit, 8 m/s at 0.9 of the width, which lands near the edge of a cymbal. The listener distance and direction can be changed while a simulation runs, and the microphone moves with them. The grid spacing (8 mm by default), the in-plane mass factor, the sample rate, and the frame rate take effect at the next Start; the Notes under the group report the grid, the frequencies at which grid dispersion lowers the partials by 3 and 10 percent, the time step, and the 60 dB decay times of the design's losses. The rigid-body projection applies to free plates only; a mounted plate takes the reaction of the strike through the mount.
69**Running.** Press Start. The stick strikes the plate at rest in its stress-free shape, and the recording of audio and movie frames starts at the impact. The view shows the total shape by default (profile plus vibration); untick Show profile to see the vibration alone, which makes a small vibration on a curved cymbal visible. Pause halts the run temporarily; Stop ends it, keeps the audio and movie, and unlocks the script and the fields. To extend a finished run, raise the duration and press Continue. After the run, drag the time slider to explore the movie; Play starts the audio from the slider position and moves the movie and the spectrogram cursor with it; Live returns to the running view and Design to the stress-free shape. The audio can be played or downloaded as a 16-bit WAV at any time; Absolute level maps 1 Pa to full scale shifted by the gain slider (the peak level in dBFS is shown with a clipping warning), and Loudness and Peak normalize each run instead.
71**URL parameters.** `?preset=crash-16` selects a preset; any strike, listener, or numerics parameter can be preset in SI units, for example `?preset=ride-20&spacing=0.006&duration=3&autostart=1`.
7710254Self-contained design scripts; 3D by default; resizable, less verbose panelJeremy Magland 73The default grid spacing of 8 mm keeps the simulation interactive but lowers the bending partials by a few percent near 1 kHz and by 10 percent a few kilohertz higher, depending on the thickness; the Notes under the grid group report the two frequencies. The stable time step is set by the in-plane wave speed (about 1.3 microseconds at 8 mm for steel and 1.7 for bronze) and does not depend on the thickness, so a thick ride costs the same per step as a thin crash; a 16 inch cymbal at 8 mm has about 2000 nodes and runs at 60 to 70 thousand steps per second on an integrated GPU, or roughly 50 s for 4 s of sound.
75## Tests
77`npm test` runs three groups of tests with Node's built-in runner.
79- `tests/cpuRef.test.ts` checks the float64 reference of the spatial discretization without a GPU: symmetry of the stiffness with exactly three rigid modes per block on uniform, tapered, elliptical, and holed plates, forces as the exact gradient of the discrete energy (flat and bowed, uniform and tapered, free and mounted), dissipativity of the loss terms and the mount's dashpot, the scaling of the bending frequencies with the thickness, the mount's stiffness against rigid translation and tilt, and convergence of the free square and circular plate frequencies to the reference values of Gorman and Leissa.
7710254Self-contained design scripts; 3D by default; resizable, less verbose panelJeremy Magland 80- `tests/design.test.ts` checks the design script: the functions a script defines for itself (including a handle that calls one defined below it), compilation and the checks with their messages, the evaluation on the grid (including the orientation of the sampled matrices), the mask of a disc with a hole, the removal of the mean plane, the mount patch, and that every preset evaluates on the default grid.
1ee087eCymbal designer: scripted design, thickness field, mount, single strikeJeremy Magland 81- `tests/gpu.test.ts` serves the app with Vite and drives a headless Chromium through Playwright. It compares one step of the WGSL kernel with the float64 reference on flat, bowed, tapered, mounted, and elliptical presets (`?selftest=1` in the browser shows the same numbers), runs a short strike on a free plate and on a mounted cymbal to completion, and checks that a script with an error is reported and cannot start. It needs Playwright's Chromium (`npx playwright install chromium`) and a machine whose Chromium has WebGPU; `npm run test:cpu` runs only the first two groups.
19b58d0Add a throughput benchmark scriptJeremy Magland 83## Benchmark
85`npm run bench` runs the crash preset in headless Chromium at several grid spacings and with several concurrent runs and prints steps per second and the wall time per second of audio (`--quick` for a short version, `--browsers N` to spread the concurrent runs over N Chromium instances; on Node 22 add `--experimental-strip-types`). The kernel is latency-bound on grids of a few thousand nodes: an RTX 6000 Ada runs the 16 inch crash at 8 mm at about 140k steps per second, 2.2 times an Intel Iris Xe, and at 4 mm at about the same rate, 5 times the integrated GPU; concurrent runs on one card add only about 40 percent in total, since the card's dispatch rate is the limit.
89- `src/params.ts`: simulation parameters, grid and mask, the plate as evaluated on the grid, material functions, patches of the strike and the mount, grid dispersion estimate.
8db04e0Design scripts in MATLAB syntax, run by numblJeremy Magland 90- `src/design.ts`: running the script with numbl, the checks on the design struct, evaluation on the grid.
7710254Self-contained design scripts; 3D by default; resizable, less verbose panelJeremy Magland 91- `src/mruntime.ts`: the one internal `.m` file the simulator puts in scope of a script, which samples a field on the grid.
1ee087eCymbal designer: scripted design, thickness field, mount, single strikeJeremy Magland 92- `src/presets.ts`: the preset scripts.
93- `src/sim/cpuRef.ts`: float64 reference of the spatial discretization; used for the time-step estimate and the tests.
94- `src/sim/shaders/`: WGSL kernels (time step, rigid-body sums and projection, listener sum, display extraction).
95- `src/sim/simulation.ts`: WebGPU driver (buffers, batch encoding, readback).
96- `src/selftest.ts`: the in-browser GPU-versus-CPU comparison.
6201bf2Show the mount and the listener in the 3D view; lay the plate flatJeremy Magland 97- `src/render/`: 2D and 3D WebGPU rendering of the plate, with the markers of the mount, the strike point, and the listener.
1ee087eCymbal designer: scripted design, thickness field, mount, single strikeJeremy Magland 98- `src/audio/`: FFT, spectrogram, WAV encoder, playback, level mapping.
99- `src/ui/editor.ts`, `src/ui/form.ts`, and `src/main.ts`: the script editor, the parameter form, and the rest of the interface.
100- `tests/`: the three test files above.
↑↓ move↵openescclose