From 2195ecce942817235cadc3f2541823fbd314e0b9 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 18:41:41 +0000 Subject: [PATCH] Lobby: dice sounds Clatter per die and a result cue for checks, synthesized with the Web Audio API; mute button and volume slider next to the dice. Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01HJhXuLD2Z6hv3Wqeq9UL9K --- README.md | 1 + frontend/src/Lobbies.jsx | 9 +++++ frontend/src/sound.js | 87 ++++++++++++++++++++++++++++++++++++++++ frontend/src/styles.css | 2 + 4 files changed, 99 insertions(+) create mode 100644 frontend/src/sound.js diff --git a/README.md b/README.md index f05b30a..4c29744 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ The lobby page is built for desktop with three columns: the **character sheet** - **Skill and attribute checks**: players click an attribute or an unlocked skill on their own sheet. The server rolls a d100 (roll under): target = attribute or skill total + the DM's difficulty + the character's personal modifier. 01-05 is always a critical success, 96-100 always a critical failure. The chat and the table show the roll, the target and by how much it succeeded or failed. Combat is not handled specially: roll the normal skill and let the DM adjust with the modifiers. - **Difficulty** (bar under the characters): the DM picks a level from the rules table (Trivial +60 ... Hellish -60) or a custom number. It applies to all checks until changed. With **Hide numbers from players** the players only see their roll and success / failure, not the target, difficulty or margin. - **Dice** (below the table): d4 ... d100 with a count (1 to 10 dice). The server rolls, the dice tumble into the middle of the table in the colour of whoever threw them, and the result goes into the chat. With a person selected as recipient in the chat dropdown it is a private roll. **DM rolls** have three modes: *Mystery* (default: players see grey dice with "?" but not the result), *Hidden* (only the DM sees it) and *Public*. +- **Sound**: dice clatter when anything is rolled (yours, other players' and DM rolls, mystery rolls too), plus a short cue for the result of a check (rising chime for a critical success, soft ping for success, low tone for failure, dark double tone for a critical failure). The sounds are generated in the browser, there are no sound files. The speaker button next to the dice mutes them, the slider sets the volume; both are remembered per browser. Browsers only allow sound after the page was clicked once, and nothing plays while the tab is in the background. - **Own dice**: with this switch on, a click on a die or a skill asks for the result of your real dice instead of rolling. The server only checks the range and marks the roll as "own dice" in the chat. - **Colours**: every player has a different colour (lobby menu -> Your colour); the DM is gold. - **Lobby menu** (top right): join code, rename, remove players, colour, switch character, leave or delete the lobby. diff --git a/frontend/src/Lobbies.jsx b/frontend/src/Lobbies.jsx index 2b7248e..922f6b8 100644 --- a/frontend/src/Lobbies.jsx +++ b/frontend/src/Lobbies.jsx @@ -1,5 +1,6 @@ import { useCallback, useEffect, useRef, useState } from 'react' import PaperSheet, { ThemeSwitch } from './PaperSheet.jsx' +import { sound, playRoll } from './sound.js' import { MapView, MapLibrary, TokenForm, WallLayer } from './MapView.jsx' const J = { 'Content-Type': 'application/json' } @@ -303,6 +304,8 @@ function DieIcon({ sides }) { const MODES = [['mystery', 'Mystery', 'Players see grey dice, but not the result'], ['hidden', 'Hidden', 'Only you see the roll'], ['public', 'Public', 'Everybody sees the roll']] function DiceBar({ to, me, isDm, count, setCount, mode, setMode, own, setOwn, onRoll, busy }) { + const [snd, setSnd] = useState(sound.enabled) + const [vol, setVol] = useState(sound.volume) return (