{"_id":"@amplib/sound-synthesis","_rev":"2-a06cc1108ef001890c4bca6cf771c3df","name":"@amplib/sound-synthesis","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@amplib/sound-synthesis","version":"0.1.0","author":{"name":"Another Machine"},"license":"Apache-2.0","_id":"@amplib/sound-synthesis@0.1.0","maintainers":[{"name":"jakealbaugh","email":"jake.albaugh@gmail.com"}],"homepage":"https://amplib.app/sound-synthesis","bugs":{"url":"https://github.com/another-machine/public-library/issues"},"dist":{"shasum":"516e73aeb95c3bc874d9d6d6a533de4dce4487c2","tarball":"https://registry.npmjs.org/@amplib/sound-synthesis/-/sound-synthesis-0.1.0.tgz","fileCount":6,"integrity":"sha512-ZkRaN3PrhQFavHbvLUHLK6pHGLFHV5/qqHoreL+SFmqNn4Nnw9JKWGUtABff6B0aftH3S4fwhlAK3ctRfREJsw==","signatures":[{"sig":"MEUCIQD59Obfk81IONMsiVN74iYixRd7Jlt29m9UwfZNdBWRbAIgGqycfYcHHFp1PbxlAZHEGHdTmzCcomSdorOOeLAHY3o=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":233233},"main":"dist/index.js","tsup":{"dts":true,"clean":true,"entry":["src/index.ts"],"format":"esm","minify":false,"external":["@amplib/music-theory"],"sourcemap":true,"splitting":false},"type":"module","types":"dist/index.d.ts","gitHead":"613df72092ceda0557612b76ee7b1b78184eadc9","scripts":{"build":"tsup","watch":"tsup --watch","prepack":"npm run build"},"_npmUser":{"name":"jakealbaugh","email":"jake.albaugh@gmail.com"},"repository":{"url":"git+https://github.com/another-machine/public-library.git","type":"git","directory":"packages/amplib-sound-synthesis"},"_npmVersion":"10.2.4","description":"Making sound with the Web Audio API. FM voices, layers, drums, and the bus topology to hang them on.","directories":{},"_nodeVersion":"20.11.0","dependencies":{"@amplib/music-theory":"^0.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/sound-synthesis_0.1.0_1785530035641_0.7409841340457382","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@amplib/sound-synthesis","description":"Making sound with the Web Audio API. FM voices, layers, drums, and the bus topology to hang them on.","version":"0.2.0","main":"dist/index.js","types":"dist/index.d.ts","publishConfig":{"access":"public"},"homepage":"https://amplib.app/sound-synthesis","repository":{"type":"git","url":"git+https://github.com/another-machine/public-library.git","directory":"packages/amplib-sound-synthesis"},"tsup":{"entry":["src/index.ts"],"clean":true,"dts":true,"format":"esm","minify":false,"sourcemap":true,"splitting":false,"external":["@amplib/music-theory"]},"scripts":{"build":"tsup","watch":"tsup --watch","prepack":"npm run build"},"author":{"name":"Another Machine"},"license":"Apache-2.0","type":"module","dependencies":{"@amplib/music-theory":"^0.1.0"},"_id":"@amplib/sound-synthesis@0.2.0","gitHead":"bb844a226c6ba5cd08d8de4ed6f53af8f18e2ad7","bugs":{"url":"https://github.com/another-machine/public-library/issues"},"_nodeVersion":"20.11.0","_npmVersion":"10.2.4","dist":{"integrity":"sha512-K3T1Sd/VlMZDPmAZvn2zQmc6fmE7qRq36RH395ILh0yFU/Y1Hhbm79bmP0Mw7bm7arf8ta9hI5aYHOvXRh2tRQ==","shasum":"0e6cf6a6e4b851809a49418247a120610ef62c52","tarball":"https://registry.npmjs.org/@amplib/sound-synthesis/-/sound-synthesis-0.2.0.tgz","fileCount":6,"unpackedSize":256785,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQC+Xba7uxij9mdK/euF3LbPge2viUeYdhh17fOy4vOXqAIgeRrj/GtdJtnR0ca9Jiaeoioet6TrRMLxkTAqH+TjObw="}]},"_npmUser":{"name":"jakealbaugh","email":"jake.albaugh@gmail.com"},"directories":{},"maintainers":[{"name":"jakealbaugh","email":"jake.albaugh@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sound-synthesis_0.2.0_1785534283475_0.9256196929538212"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-31T20:33:55.508Z","modified":"2026-07-31T21:44:43.810Z","0.1.0":"2026-07-31T20:33:55.820Z","0.2.0":"2026-07-31T21:44:43.628Z"},"bugs":{"url":"https://github.com/another-machine/public-library/issues"},"author":{"name":"Another Machine"},"license":"Apache-2.0","homepage":"https://amplib.app/sound-synthesis","repository":{"type":"git","url":"git+https://github.com/another-machine/public-library.git","directory":"packages/amplib-sound-synthesis"},"description":"Making sound with the Web Audio API. FM voices, layers, drums, and the bus topology to hang them on.","maintainers":[{"name":"jakealbaugh","email":"jake.albaugh@gmail.com"}],"readme":"# @amplib/sound-synthesis\n\nMaking sound with the Web Audio API.\n\n```ts\nimport { AudioGraph, FMVoice } from \"@amplib/sound-synthesis\";\n\nconst audioContext = new AudioContext();\nconst graph = new AudioGraph({ audioContext });\ngraph.bypassInsert();\n\nconst voice = new FMVoice({\n  audioContext,\n  destination: graph.midBus,\n  ratio: 2,\n  index: 1.5,\n});\nvoice.pluck(440);\n```\n\nOrigin of the FM, layer, drum, and worklet modules:\n[another-machine/avva](https://github.com/another-machine/avva).\n\n## Modules\n\n| Module                                   | Description                                                                        |\n| ---------------------------------------- | ---------------------------------------------------------------------------------- |\n| `AudioGraph`                             | Bus and routing topology — slotting filters, master chain, swappable limiter stage  |\n| `FMVoice`                                | Two-operator FM voice with glide and a one-shot `pluck`                             |\n| `NodeTierBackend` / `WorkletTierBackend` | One five-voice FM tier, as a node graph or an AudioWorklet, behind one interface    |\n| `TapeDelay`                              | Delay with damping inside the feedback loop, mono or ping-pong                      |\n| `Reverb`                                 | Convolution reverb on a generated impulse — no file to fetch                        |\n| `NoiseLayer`                             | Looped noise through chord-tuned high-Q bandpass resonators                         |\n| `ShimmerLayer`                           | Two sine voices above the root under slow, mismatched amplitude LFOs                |\n| `DrumSynth` / `DrumMachine`              | Sample-free percussion and a lookahead scheduler for it                             |\n| `ChromaticWall`                          | A drifting wall of scale notes with a sparser layer of high twinkles                |\n| `Clock`                                  | Worker-driven beat clock with swing                                                 |\n| `WorkletHost`                            | Loads the fm-tier, ks-string, and lookahead-limiter worklets                        |\n\n[Live demo](https://amplib.app/sound-synthesis) — an arpeggio through pooled\nFM voices over a scheduled drum pattern, with the lookahead limiter reporting\nLUFS as it runs.\n\nThere is also a standalone `demo` directory for `ChromaticWall`. It imports\nfrom `dist`, so run `npm run build` before opening it.\n\n## Design\n\n**Worklets ship as source strings, not as URLs.** `WorkletHost` blobs the\nworklet text and calls `addModule` on the blob URL, the same way `Clock` carries\nits timing worker. The alternative — importing the worklet file and letting the\nbundler emit a URL — ties the package to one bundler: `?url` is Vite,\n`new URL(..., import.meta.url)` needs Parcel's worklet transformer, and the\nglobal bundle published to `amplib.app/lib` has no bundler at all. A blob URL\nworks in all three.\n\n`SoundTransformation` in the sibling package takes the opposite approach and\nasks its caller for a path or a script tag. That is the right call for a\nprocessor a consumer might want to swap out; these three are fixed parts of the\nsynth, so the package owns them.\n\n**Every worklet load can fail without taking the sound with it.** The loaders\nreturn `false` rather than throwing, and each has a fallback already wired:\n`AudioGraph` runs a `DynamicsCompressor` until `swapToWorkletLimiter` replaces\nit, and `NodeTierBackend` is a complete implementation of the same interface as\n`WorkletTierBackend`. Worklets fail for reasons that have nothing to do with the\ncode — a cross-origin bundle, a strict CSP, an older browser — so nothing here\ntreats them as guaranteed.\n\n**The effects are sends, not inserts.** `TapeDelay` and `Reverb` output wet\nonly, and the caller keeps its own dry path. A class that mixes dry internally\ncan serve an insert but not a send, whereas a wet-only one serves both — an\ninsert is a send plus a dry gain the caller already has. It also means the two\ncompose without arguing about who owns the dry signal: routing the delay's\noutput into the reverb's input puts the repeats inside the room, and neither\nclass needed to know the other exists.\n\n`AudioGraph` has been shaped for these since before they existed — it pads 6 dB\nof headroom ahead of its insert point and makes it back up after, and\n`updateAutoMakeup` carries coefficients measured against a chain that lived in a\nconsuming app rather than here. These two are the beginning of closing that gap.\n\n**Pitched voices are pooled; percussion is not.** `FMVoice` starts its\noscillators once and keeps them running, because `pluck`'s 3 ms retrigger\ncrossfade only works on a carrier that is already going, and because dense\npassages would otherwise allocate an oscillator pair per note. `DrumSynth` does\nbuild nodes per hit, which is correct there: drum hits are short and sparse, so\nthey never stack up, and each wants its own envelope from silence.\n\n**Two writers never share an AudioParam.** `AudioGraph` keeps `masterTrim`,\n`dimGain`, and `tremoloSum` as separate nodes rather than one gain, because\n`setTargetAtTime` from two sources on one param means the last write wins and\nthe other silently stops working — a bug with no error attached to it.\n\n### A note on `ChromaticWall`\n\nIts modulator used to run at a fixed 14.3 Hz into `carrier.detune` at a depth of\n±10 cents, which is a slow vibrato rather than frequency modulation. It now\nplays through a pool of `FMVoice`, so the modulator tracks a ratio of the\ncarrier and goes into `carrier.frequency`, putting real sidebands in the tone.\n\n**This changes how it sounds**, and the settings changed shape with it:\n`modulationDepth` and `modulationFrequency` are gone, replaced by `ratio` and\n`index`. Set `index` to 0 on either synth for a plain oscillator. The\nconstructor and `tick` signatures are unchanged apart from an optional\n`voiceCount`.\n","readmeFilename":"README.md"}