opolyx has a voice now: sound effects and a mute button
The game was completely silent. Now dice, pawns, cash, auction ticks and the win sting all make a sound — with one button to turn it all off.

In plain words
Until this update opolyx made no sound at all. Not a quiet game — a literally silent one: there was no audio anywhere in the app. Rolling a six and getting bankrupted felt exactly the same.
Now the moments that matter are audible. Nothing loops, nothing plays in the background — every sound is tied to something that just happened on the board:
- the dice, the moment you press Roll — and again as they settle on their numbers;
- your pawn, one step per cell as it moves around the board;
- money changing hands: rent, tax, buying a field, building;
- the auction clock ticking through the last five seconds of your bid;
- the Jackpot wheel, and the sting when you win or lose a game.
Sound is on by default. If you would rather play silently, the speaker button in the top bar turns everything off in one click, and your choice is remembered on that device — this is the first setting opolyx has ever stored for you.
The highlight clips we post to YouTube, Instagram and TikTok were silent too. They now carry the same cues as the game, so a bankruptcy in a clip sounds like a bankruptcy at the table.
For the technically curious
Playback is Web Audio rather than one audio element per cue. Decoded buffers give polyphony (a dice landing over a move step), sample-accurate scheduling, and stay inside the existing content-security policy — every file is served from our own domain.
Which cue plays for which event is an exhaustive switch over the engine’s event union, so adding a new domain event fails typecheck until someone decides whether it makes a sound:
// lib/sound/events.ts — exhaustive over DomainEvent['kind']
export function soundForEvent(e: DomainEvent, viewerSlot: number | null): SoundId | null {
switch (e.kind) {
case 'rent_paid':
return e.payload.amount >= BIG_RENT ? 'cash-big' : 'cash';
case 'player_bankrupt':
return 'bankrupt';
// …every kind handled; no default, so a new event is a type error
}
}Four cues are timing-driven rather than event-driven, because the server event is not when the player perceives the action. The roll fires at click time (0 ms), the landing fires off the 3D dice settle, the step fires per cell in the pawn hop queue, and win/lose fire when the result screen mounts.
| Cue | Fires on |
|---|---|
| dice-roll | the click, not the server response |
| dice-land | the end of the 3D dice settle animation |
| move-step | each cell in the pawn hop queue |
| tick | the last 5 seconds of your own turn or bid |
| victory / defeat | the result screen mounting |
All 26 files are CC0, trimmed, peak-normalised to −3 dBFS and encoded as mono MP3 — MP3 rather than Ogg because iOS Safari cannot decode Ogg Vorbis. The build script asserts the encoded peak after encoding: an earlier version shipped files with the right duration, the right size, and no audible sound at all, because a fade filter had silenced everything past its window.
