Skip to content

perry/audio: add cross-platform sound playback module #1867

Closed

Description

@TheHypnoo

Problem

Perry currently has no first-class audio module. Apps that need to play sounds — short SFX (notifications, UI feedback, games), background music loops, voice prompts, accessibility cues — have to drop down to FFI per platform, which defeats the cross-platform promise for an extremely common need.

This affects games, productivity apps with audio feedback, media tools, alarm/timer apps, learning apps, accessibility tooling, and anything that ships across web + desktop + mobile.

Proposal

A small, focused module — explicitly not a full DAW API. Scope: "load a sound, play it, stop it, set volume, loop it":

import { loadSound, playSound, stopSound, setVolume, Sound } from "perry/audio";

const click: Sound = await loadSound("assets/click.wav");
playSound(click);                  // fire-and-forget
playSound(click, { volume: 0.5 }); // per-play options
playSound(click, { loop: true });

setVolume(click, 0.8);
stopSound(click);

Supported formats: at least WAV and MP3 on every platform; OGG where the platform supports it natively.

Asset resolution: paths relative to the app bundle on native, relative to the HTML on web, with the same call site.

Implementation sketch

Platform Mechanism
macOS / iOS / tvOS AVAudioPlayer (simple) or AVAudioEngine (mixing). AVAudioPlayer is fine for v1.
Linux PulseAudio / PipeWire via a thin C wrapper, or libcanberra for short sounds.
Windows XAudio2 (modern) or PlaySound for the absolute minimal subset.
Android SoundPool for short SFX, MediaPlayer for longer streams.
Web (WASM) HTML <audio> for one-shots, Web Audio API (AudioContext + AudioBufferSourceNode) for low-latency overlapping playback.

The web bridge already has Web Audio capture imports, so the playback side is a natural extension of the same wasm_runtime.js surface.

Open questions

  • Should playSound return a handle to the playback instance (so multiple overlapping plays of the same sound can be stopped independently), or stick with sound-level handles for simplicity in v1?
  • Volume: per-sound, per-playback, or both? A global setMasterVolume is probably also wanted.
  • Streaming vs. preload: do we expose both, or always preload and add streaming later for long files?

Activity

  1. self-assigned this
    on May 26, 2026
  2. added 4 commits that reference this issue on May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions