Audio
If you record voice-over, Patterpad’s Audio Folders lets you keep
takes as <beatId>.wav (or .mp3) files, ranked by recording status (scratch < recorded < final). At runtime your game needs to play the best available take for each line, without
reimplementing that folder-ranking search. Patterplay makes that turnkey.
How it works
Section titled “How it works”The ranking is resolved at build time, not in your game. When you Build (or run Production ▸
Update Audio Manifest), Patterpad writes a small sidecar next to your audio, patteraudio.json:
{ "schema": "patter/audio@0", "clips": { "L1": { "file": "final/L1.wav", "status": "final" }, "L2": { "file": "scratch/L2.mp3", "status": "scratch" } } }Each beat maps to the winning file (highest recorded rung), as a path relative to your audio root.
It’s a sidecar, never baked into the .patterc, so new takes don’t force a story rebuild.
To ship: drop the audio folder (subfolders + patteraudio.json) wherever your engine reads data, then
point a resolver at it. The resolver resolves a path; it does not play the audio: playback is
engine-native (and can’t be portable), but the part teams get stuck on, finding the right file, is done
for you.
Wire it into your game
Section titled “Wire it into your game”Each engine has the same tiny resolver: build it from the manifest + the base path where you deployed
the audio, then resolve(beatId) returns the full path/URL (or nothing when a line has no recording).
JavaScript (@patterkit/play-helpers):
import { createAudioResolver } from "@patterkit/play-helpers";
const manifest = await (await fetch("audio/patteraudio.json")).text();const audio = createAudioResolver(manifest, "audio"); // base = where you served the folder
const step = flow.advance();const src = audio.resolve(step.id); // "audio/final/L1.wav", or nullif (src) new Audio(src).play(); // playback is yoursUnity (C#), deploy the folder under StreamingAssets:
var json = File.ReadAllText(Path.Combine(Application.streamingAssetsPath, "audio", "patteraudio.json"));var audio = new PatterAudioResolver(json, Path.Combine(Application.streamingAssetsPath, "audio"));string path = audio.Resolve(step.Id); // full path, or nullUnreal (C++ / Blueprint): UPatterAudio is BlueprintCallable, so this can be graph-only:
UPatterAudio* Audio = UPatterAudio::Load(ManifestJson, TEXT("Audio"));FString Path = Audio->Resolve(Step.Id); // full path, or emptyGodot (GDScript), deploy under res:// (or user://):
var json := FileAccess.get_file_as_string("res://audio/patteraudio.json")var audio := PatterAudio.new(json, "res://audio")var path := audio.resolve(step.get("id", "")) # full path, or "" when noneif path != "": my_player.stream = load(path)- Resolve, not play. Every engine returns a path/URL; you load and play it your way. That boundary is deliberate, audio playback isn’t portable, but resolution is.
- No search shipped. The winner is chosen at build time, so the runtime just does a map lookup.
- No recording?
resolvereturns null / empty for a beat with no take, so you can fall back to on-screen text or silence. - Single language for now (VO isn’t localised per-locale yet).
- Keep it fresh. Re-run Update Audio Manifest (or Build) after adding takes so
patteraudio.jsonreflects the latest winners.
MIT-licensed open source · Made by Ian Thomas · patterkit.com