{"_id":"@emilioocelotl/spaudio","_rev":"2-8f140159b38af8943f7182d7b9cf94a3","name":"@emilioocelotl/spaudio","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@emilioocelotl/spaudio","version":"0.1.0","keywords":["audio","spatial","web-audio-api","panning","dbap","multichannel"],"license":"MIT","_id":"@emilioocelotl/spaudio@0.1.0","maintainers":[{"name":"emilioocelotl","email":"emilio.ocelotl@gmail.com"}],"homepage":"https://github.com/emilioocelotl/spaudio#readme","bugs":{"url":"https://github.com/emilioocelotl/spaudio/issues"},"dist":{"shasum":"bca892efd1af0ed4bdcae213b8f1c7c520e19e93","tarball":"https://registry.npmjs.org/@emilioocelotl/spaudio/-/spaudio-0.1.0.tgz","fileCount":7,"integrity":"sha512-ud1SQs3tu3QFjZSMibEEG07UGHkOcC7cJ+D9Kltwu6o/vy8eMurqSYUv2Yd2giywQjaW7DRclIQpABe93YlIIA==","signatures":[{"sig":"MEYCIQDIwDGFUEjWvXN/E0L5GXZhb7DJsfodsdNCpPxiU4W9xgIhAL+mcK8EafuhoExQQLvKNhOACPSw2p2/OpoEEiS8/C34","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":11124},"main":"./src/index.js","type":"module","exports":{".":"./src/index.js"},"gitHead":"a0537e46dd695c107072c201a4df1b3947f5e306","_npmUser":{"name":"emilioocelotl","email":"emilio.ocelotl@gmail.com"},"repository":{"url":"git+https://github.com/emilioocelotl/spaudio.git","type":"git"},"_npmVersion":"9.2.0","description":"Spatial audio classes for the Web Audio API","directories":{},"_nodeVersion":"22.22.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/spaudio_0.1.0_1778264903940_0.9026211076556305","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@emilioocelotl/spaudio","version":"0.2.0","description":"Spatial audio classes for the Web Audio API","type":"module","main":"./src/index.js","exports":{".":"./src/index.js"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/emilioocelotl/spaudio.git"},"keywords":["audio","spatial","web-audio-api","panning","dbap","multichannel"],"gitHead":"127121e3d320b015da742052cdcf6404649d8ab9","bugs":{"url":"https://github.com/emilioocelotl/spaudio/issues"},"homepage":"https://github.com/emilioocelotl/spaudio#readme","_id":"@emilioocelotl/spaudio@0.2.0","_nodeVersion":"22.22.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-ZixTQmxCyrQOQnsTCUfb+0I+oOz0c90VHpAeQsA3ygs/PMycw11K+rx3FeGm1dIFwoTiYUbvMCghxEA739DkGg==","shasum":"c962daa368462ae36784da10ed8b567b93a243a9","tarball":"https://registry.npmjs.org/@emilioocelotl/spaudio/-/spaudio-0.2.0.tgz","fileCount":7,"unpackedSize":11752,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDW27VpNLXd7nP3sa4p10qSLs81dwby7NRMR84M/aePYgIhANwuXzIEn2vMjAilnY2L9qrsGKIwXCS1FWjn1a1saRWg"}]},"_npmUser":{"name":"emilioocelotl","email":"emilio.ocelotl@gmail.com"},"directories":{},"maintainers":[{"name":"emilioocelotl","email":"emilio.ocelotl@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/spaudio_0.2.0_1780943167650_0.49082217830754393"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-08T18:28:23.886Z","modified":"2026-06-08T18:26:07.856Z","0.1.0":"2026-05-08T18:28:24.100Z","0.2.0":"2026-06-08T18:26:07.770Z"},"bugs":{"url":"https://github.com/emilioocelotl/spaudio/issues"},"license":"MIT","homepage":"https://github.com/emilioocelotl/spaudio#readme","keywords":["audio","spatial","web-audio-api","panning","dbap","multichannel"],"repository":{"type":"git","url":"git+https://github.com/emilioocelotl/spaudio.git"},"description":"Spatial audio classes for the Web Audio API","maintainers":[{"name":"emilioocelotl","email":"emilio.ocelotl@gmail.com"}],"readme":"# spaudio\n\nSpatial audio for the Web Audio API. Multichannel panning, flexible speaker layouts, offline rendering to WAV.\n\n> Status: pre-1.0. API may change between minor versions.\n\n## Install\n\n```\nnpm install @emilioocelotl/spaudio\n```\n\n## Quick start\n\n```js\nimport { SpatialLayout, SpatialRouter, SpatialSource } from '@emilioocelotl/spaudio';\n\nconst ctx = new AudioContext();\nconst layout = SpatialLayout.octo();\nconst router = new SpatialRouter({ context: ctx, layout });\nrouter.output.connect(ctx.destination);\n\nconst buffer = await fetch('sample.wav').then(r => r.arrayBuffer()).then(b => ctx.decodeAudioData(b));\nconst node = new AudioBufferSourceNode(ctx, { buffer });\n\nconst source = new SpatialSource({ node, position: { x: 0.5, y: -0.3 } });\nrouter.add(source);\nnode.start();\n\nsource.setPosition({ x: -0.2, y: 0.4 });\n```\n\n## Coordinate system\n\nPositions are normalized to `[-1, 1]` on each axis. Origin `(0, 0)` is the centroid of the layout. `x` grows to the right, `y` grows forward (away from the listener). `z` is reserved for future height support.\n\n## API\n\n### `SpatialLayout`\n\nImmutable description of a speaker arrangement.\n\n```js\nSpatialLayout.stereo();\nSpatialLayout.quad();                    // 4 speakers, 90° apart\nSpatialLayout.octo();                    // 8 speakers, 45° apart\nnew SpatialLayout(speakers);             // custom\n```\n\n`speakers` is an array of `{ angle, radius, channel }`:\n- `angle` — degrees, 0° = front, clockwise\n- `radius` — normalized distance from center, typically `1`\n- `channel` — output channel index (0-based)\n\nProperties:\n- `layout.channels` — number of output channels\n- `layout.speakers` — readonly array of speaker descriptors\n\n### `SpatialSource`\n\nWraps a Web Audio source node with spatial metadata.\n\n```js\nconst source = new SpatialSource({\n  node,                           // any AudioNode\n  position: { x: 0, y: 0 }        // normalized -1..1\n});\n\nsource.setPosition({ x, y });     // triggers router update if attached\nsource.position;                  // { x, y }\nsource.node;                      // underlying AudioNode\n```\n\n### `SpatialRouter`\n\nRoutes sources to the layout's output channels using a panning algorithm.\n\n```js\nconst router = new SpatialRouter({\n  context,                        // AudioContext or OfflineAudioContext\n  layout,                         // SpatialLayout\n  mode: 'dbap'                    // 'dbap' (default) | 'vbap' | 'equal-power'\n});\n\nrouter.add(source);\nrouter.remove(source);\nrouter.output;                    // AudioNode with layout.channels outputs\nrouter.gainSnapshot(source);      // number[] — current gain per speaker, for visualizations\nrouter.schedulePosition(source, { x, y }, time); // schedule DBAP gains at an explicit audio time\n```\n\nConnect `router.output` to `context.destination` for realtime playback, or let it feed an `OfflineAudioContext` destination for rendering.\n\nUse `schedulePosition()` instead of `source.setPosition()` inside `SpatialRenderer.toWav()` callbacks — during offline setup `currentTime` is always `0`, so `setPosition()` would collapse all keyframes to the same instant.\n\n### `SpatialRenderer`\n\nHelper for offline rendering to WAV.\n\n```js\nimport { SpatialRenderer } from '@emilioocelotl/spaudio';\n\nconst wavBlob = await SpatialRenderer.toWav({\n  layout: SpatialLayout.octo(),\n  duration: 420,                  // seconds\n  sampleRate: 48000,\n  schedule: (ctx, router) => {\n    // Build the scene inside the OfflineAudioContext.\n    // Add sources to `router`, schedule `.start(time)` on nodes.\n    // Use router.schedulePosition(source, { x, y }, time) to animate positions.\n  }\n});\n```\n\nReturns a `Blob` ready for download or upload.\n\n## Planned\n\nFuture classes, not yet implemented:\n\n- `SpatialListener` — listener position and orientation for off-center audiences\n- `SpatialScene` — aggregate of sources, listener, router, layout\n- `SpatialBus` — submix with spatial metadata\n- Ambisonic encoding (AmbiX, up to 5th order)\n\n## Requirements\n\n- Web Audio API (all modern browsers)\n- Multichannel playback requires Chrome 110+ with a multichannel audio interface recognized by the OS\n- Offline rendering works on any browser supporting `OfflineAudioContext`\n\n## License\n\nMIT\n","readmeFilename":"README.md"}