# Maraca A physically-based simulation of a maraca shake pattern, sampled as acoustic pressure at a point in space. A few hundred grains inside a closed spherical shell produce a few thousand stochastic grain-shell impacts across the strokes of the gesture; each impact is a Hertz contact that drives the shell's spherical-harmonic modes, and the modes radiate to a listener point through the exact multipole transfer of a sphere. There is no microphone model: no directivity, no capsule response, no gain staging. The output is p(r, t) in pascals. ## Contents | Path | What it is | |---|---| | `matlab/` | The algorithm in MATLAB syntax, run by [numbl](https://numbl.org) | | `instrument/` | The model behind the track-render instrument interface used by [midiboot](../../proof-of-concept/midiboot) and by the web app (worker, note mapping, caching; `npm test` there runs the model in node) | | `webapp/` | Interactive browser app that executes the MATLAB code client-side | The MATLAB code runs from the command line with `cd matlab && numbl run maraca_demo.m`, or `maraca_validate.m` to run the checks. The web app is a Vite project; see `webapp/README.md`. ## The model The signal chain, with each stage in its own `.m` file: ``` shake gesture -> stochastic impact process maraca_impacts.m -> Hertz grain contacts on the shell wall -> spherical shell modes (two membrane branches + bending) maraca_basis.m -> exact sphere multipole radiation maraca_radiate.m -> retarded free-field pressure at the listener -> anti-aliased decimation -> PCM maraca_output.m ``` Three points are worth stating plainly, because they are where the model commits itself. **The grains are not tracked individually.** A stroke is a draw from a point process: a Poisson count of impacts whose mean is `grain_count * bounces_per_grain`, arrival times from a Gamma(3) burst after the hand reverses, Rayleigh-distributed impact speeds, and impact points scattered by a von Mises-Fisher distribution about the pole the grains are thrown at, alternating between the two poles on successive strokes. Grain sizes are lognormal. Each impact is then an independent Hertz contact, so the contact time falls as `v^(-1/5)` and harder or faster grains excite the shell higher up: timbre varies with dynamics on its own, rather than through a scripted velocity layer. What this buys is speed, a full pattern renders in well under a second, and reproducibility from a seed. What it gives up is any coupling between grains, so a shell packed tight enough for the grains to jam is outside the model. **The shell modes come from membrane theory of a complete sphere, with a bending correction.** Each degree `l` gives two branches from a quadratic in `Omega^2`; the `l = 0` upper root is the breathing mode, and the `l = 1` lower root is exactly zero, being rigid translation, which the hand holds. The lower branch flattens toward `Omega^2 = 1 - nu^2` at high `l`, so a flat-plate bending term is added to it, which is what spreads the high-degree modes upward. The interior air enters as a stiffness on the breathing mode alone, since modes with `l >= 1` displace no net volume. **Radiation uses the exact multipole transfer of a sphere**, `-i rho0 c h_l(kr) / h_l'(ka)`, evaluated at each mode's centre frequency. Because every `m` of a degree shares one frequency and one transfer, the spherical-harmonic addition theorem collapses the `m` sum to a Legendre polynomial in the angle between the impact point and the listener, so one damped resonator per retained mode carries the whole degree. Modal truncation then discards the dense high-degree modes whose collective off-resonance response is the immediate forced motion of the wall around the contact, and that broadband part is reinstated separately as a point-driven-plate term. That last term is the least principled piece of the model, and `direct_gain` turns it off. ## Validation `matlab/maraca_validate.m` (also `npm run selftest` in `webapp/`) checks the spherical Hankel functions against their closed forms and against finite differences, the breathing mode against its closed form, the Hertz `v^(-1/5)` contact-time scaling, the far-field `-6 dB` per doubling of distance, exact reproducibility of the seeded impact process, and pinned end-to-end diagnostics. Unlike the sibling `woodblock` repository, there is no separate Python reference implementation here: the MATLAB code is the reference, and the pinned numbers were recorded from it when the model was built. ## Defaults A 3.4 cm gourd shell, 320 grains at 1.3 bounces each, eight strokes at 4.5 Hz with an accent on every second stroke. That yields 3628 impacts with a 39 µs mean Hertz contact, 11 retained shell modes from 6446 Hz up, a breathing mode at 14751 Hz, and a peak of 14.9 Pa (117 dB SPL) at the 42 cm listener before normalization.