{"id":22925511,"url":"https://github.com/ringtailsoftware/zig-wasm-audio-framebuffer","last_synced_at":"2025-03-17T16:12:19.223Z","repository":{"id":136877150,"uuid":"609212117","full_name":"ringtailsoftware/zig-wasm-audio-framebuffer","owner":"ringtailsoftware","description":"Examples of integrating Zig and Wasm (and C) for audio and graphics on the web","archived":false,"fork":false,"pushed_at":"2025-01-02T00:32:45.000Z","size":53912,"stargazers_count":63,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-15T11:59:29.378Z","etag":null,"topics":["c","doom","wasm","webassembly","webaudio","zig","zig-package"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ringtailsoftware.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-03-03T15:59:29.000Z","updated_at":"2025-02-23T09:25:18.000Z","dependencies_parsed_at":"2024-12-06T01:27:03.309Z","dependency_job_id":"650f18cd-5c39-4019-aba5-c6accb092d1e","html_url":"https://github.com/ringtailsoftware/zig-wasm-audio-framebuffer","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ringtailsoftware%2Fzig-wasm-audio-framebuffer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ringtailsoftware%2Fzig-wasm-audio-framebuffer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ringtailsoftware%2Fzig-wasm-audio-framebuffer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ringtailsoftware%2Fzig-wasm-audio-framebuffer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ringtailsoftware","download_url":"https://codeload.github.com/ringtailsoftware/zig-wasm-audio-framebuffer/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244066189,"owners_count":20392406,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","doom","wasm","webassembly","webaudio","zig","zig-package"],"created_at":"2024-12-14T09:00:40.216Z","updated_at":"2025-03-17T16:12:19.203Z","avatar_url":"https://github.com/ringtailsoftware.png","language":"C++","readme":"# zig-wasm-audio-framebuffer\n\nToby Jaffey https://mastodon.me.uk/@tobyjaffey\n\nStraightforward examples of integrating Zig and Wasm for audio and graphics on the web.\n\nCompiling against Zig v0.14.0.\n\nWhere C code requires functions from the C library, https://github.com/ringtailsoftware/zeptolibc is used.\n\n# Demos\n\nVisit https://ringtailsoftware.github.io/zig-wasm-audio-framebuffer\n\n - Sinetone, simple waveform generator\n - Synth, HTML/CSS piano keyboard driving MIDI synth\n - Mod, Pro-Tracker mod player\n - Mandelbrot, mandelbrot set, mouse interaction\n - Bat, arcade style game skeleton, keyboard control, interactive graphics, background music, sound effects\n - Doom, Doom1 Shareware, keyboard control, MIDI music, sound effects\n - TinyGL, software GL renderer in Wasm\n - OliveC, graphics library with sprite blit, circle, rectangle, line, etc.\n - Agnes, NES emulator (no sound)\n - Terminal, libvterm based terminal emulator, playing a terminal tetris game\n\n## Aims\n\n - Cross-platform (running on multiple browsers and operating systems)\n - Simple understandable code. No hidden libraries, no \"emscripten magic\"\n - Main loop in browser, wasm responds to function calls to init/update/render\n - One wasm binary per program\n - Use existing C libraries to do fun things\n\n# Build and test\n\n    zig build \u0026\u0026 zig build serve -- zig-out -p 8000\n\nBrowse to http://localhost:8000\n\n## Video system\n\nAn in-memory 32bpp ARGB framebuffer is created and controlled in Zig. To render onto a canvas element, JavaScript:\n\n - Requests the framebuffer Zig/WebAssembly pointer with `getGfxBufPtr()`\n - Wraps the memory in a `Uint8ClampedArray`\n - Creates an `ImageData` from the `Uint8ClampedArray`\n - Shows the `ImageData` in canvas's graphics context\n\n## Audio pipeline\n\nAn \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/API/AudioWorkletNode\"\u003eAudioWorkletNode\u003c/a\u003e is used to move PCM samples from WebAssembly to the audio output device.\n\nThe system is hardcoded to 2 channels (stereo) and uses 32-bit floats for audio data throughout. In-memory arrays of audio data are created and controlled in Zig.\n\nThe AudioWorkletNode expects to pull chunks of audio to be rendered on-demand. However, being isolated from the main thread it cannot directly communicate with the main Wasm program. This is solved by using a \u003ca href=\"https://github.com/padenot/ringbuf.js/\"\u003eshared ringbuffer\u003c/a\u003e. To render audio to the output device, JavaScript:\n\n - Tells Zig/WebAssembly the expected sample rate in Hz for the output device, `setSampleRate(44100)`\n - Forever, checks if the ringbuffer has space for more data\n - Tells Zig/WebAssembly to fill its audio buffers with `renderSoundQuantum()`\n - Fetches pointers to the left and right channels using `getLeftBufPtr()` `getRightBufPtr()`\n - Copies from left and right channels into the ringbuffer\n\nThe `WasmPcm` (`wasmpcm.js`) class creates the `AudioWorkletNode` `WASMWorkletProcessor` using `pcm-processor.js`. At regular intervals `WasmPcm` calls `pcmProcess()` to request audio data from Wasm.\n\n# Compatibility\n\nTested on Safari/Chrome/Firefox on macOS, Safari on iPhone SE2/iPad, Chrome/Android Galaxy Tablet\n\n# iOS unmute\n\nBy default web audio plays under the same rules as the ringer. The `unmute.js` script loops a constant silence in the background to force playback while the mute button is on.\n\n# CORS (Cross-Origin Resource Sharing)\n\nTo share data between the main thread and the worklet, SharedArrayBuffer is used. This requires two HTTP headers to be set:\n\n    Cross-Origin-Opener-Policy: same-origin\n    Cross-Origin-Embedder-Policy: require-corp\n\nHowever, this is worked around by using \u003ca href=\"https://github.com/gzuidhof/coi-serviceworker\"\u003ecoi-serviceworker\u003c/a\u003e which reloads the page on startup.\n\n","funding_links":[],"categories":["Network \u0026 Web"],"sub_categories":["WebAssembly"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fringtailsoftware%2Fzig-wasm-audio-framebuffer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fringtailsoftware%2Fzig-wasm-audio-framebuffer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fringtailsoftware%2Fzig-wasm-audio-framebuffer/lists"}