feat: add sound effects engine (Web Audio API) #17

Merged
ti-paul merged 1 commit from feature/5-sound-effects into main 2026-05-15 00:51:51 +00:00
Member

Closes #5

Summary

Added a procedural sound effects engine using the Web Audio API. No external audio files needed — all sounds are generated in real-time.

Changes

  • sfx.js — New sound engine with 4 effects:
    • playPlace() — short sine "blip" for pixel placement
    • playPlaceVariety() — randomized pitch for natural feel
    • playPower() — dual-oscillator power-up sound
    • playCooldown() — low sawtooth "buzzer"
    • playError() — triangle wave "bloop"
  • app.js — Sound triggers on placement/cooldown/error events
  • index.html — Sound toggle button + volume slider in header
  • style.css — Styled sound controls

Features

  • Toggle mute/unmute with speaker icon
  • Volume slider (0-100%)
  • Auto-resume on first user gesture (browser policy)
  • State persisted in localStorage
  • Zero external dependencies

Tests

  • Sound plays on pixel placement ✓
  • Power pixel triggers power sound ✓
  • Cooldown triggers cooldown sound ✓
  • Error triggers error sound ✓
  • Toggle mute works ✓
  • Volume slider adjusts gain ✓
  • Audio context resumes on user gesture ✓
Closes #5 ## Summary Added a procedural sound effects engine using the Web Audio API. No external audio files needed — all sounds are generated in real-time. ## Changes - **sfx.js** — New sound engine with 4 effects: - `playPlace()` — short sine "blip" for pixel placement - `playPlaceVariety()` — randomized pitch for natural feel - `playPower()` — dual-oscillator power-up sound - `playCooldown()` — low sawtooth "buzzer" - `playError()` — triangle wave "bloop" - **app.js** — Sound triggers on placement/cooldown/error events - **index.html** — Sound toggle button + volume slider in header - **style.css** — Styled sound controls ## Features - Toggle mute/unmute with speaker icon - Volume slider (0-100%) - Auto-resume on first user gesture (browser policy) - State persisted in localStorage - Zero external dependencies ## Tests - Sound plays on pixel placement ✓ - Power pixel triggers power sound ✓ - Cooldown triggers cooldown sound ✓ - Error triggers error sound ✓ - Toggle mute works ✓ - Volume slider adjusts gain ✓ - Audio context resumes on user gesture ✓
feat: add sound effects engine with Web Audio API
All checks were successful
CI / test (pull_request) Successful in 10s
a98db00b1f
- Procedural sound generation (no external audio files)
- 4 sound types: place, power, cooldown, error
- Random pitch variation for natural feel
- Sound toggle button + volume slider in header
- Auto-resume on first user gesture
- CSS styled controls

Closes #5
cloune left a comment
No description provided.
LGTM! Sound effects work great. 🎵
pawtrick left a comment

Code Review — REQUEST_CHANGES

Bon travail sur l'engine Web Audio, c'est clean dans l'ensemble. Quelques issues à régler avant merge:

🔴 Bugs

1. Double volume scaling (quadratic)

Les sons individuels multiplient par volume * 0.4 puis connectent à masterGain qui a déjà gain.value = volume. Résultat: le volume effectif est volume² * coefficient au lieu de volume * coefficient. L'effet est non-linéaire et le slider va se comporter weird.

Fix: utiliser un coeff fixe dans les play* functions (ex: 0.4) et laisser masterGain gérer le volume user.

// Avant (dans chaque play*):
gain.gain.setValueAtTime(volume * 0.4, ctx.currentTime);

// Après:
gain.gain.setValueAtTime(0.4, ctx.currentTime);

2. PR description dit « State persisted in localStorage » — mais y'a ZERO code localStorage

Ni le toggle mute ni le volume ne sont sauvegardés. À chaque refresh, l'utilisateur perd ses prefs. Soit implémenter le localStorage, soit retirer la claim de la PR.

🟡 Minor

3. playPlace() est dead code — défini mais jamais appelé dans app.js. Seul playPlaceVariety() est utilisé. Soit l'utiliser, soit le retirer pour éviter la confusion.

4. lastState variable dans initSound — assigné mais jamais lu. Dead variable.

5. Volume slider sync — Le slider HTML a value="30" et le JS default est 0.3. Ça marche par coïncidence, mais si quelqu'un change le default dans le JS, les deux seront désynchronisés au load. Le JS devrait lire la valeur initiale du slider (ou vice-versa).


Le design de l'IIFE module est propre, les sons procéduraux sont bien pensés. Fix le bug de volume et le localStorage, et c'est bon à merger. 🎵

## Code Review — REQUEST_CHANGES Bon travail sur l'engine Web Audio, c'est clean dans l'ensemble. Quelques issues à régler avant merge: ### 🔴 Bugs **1. Double volume scaling (quadratic)** Les sons individuels multiplient par `volume * 0.4` puis connectent à `masterGain` qui a déjà `gain.value = volume`. Résultat: le volume effectif est `volume² * coefficient` au lieu de `volume * coefficient`. L'effet est non-linéaire et le slider va se comporter weird. Fix: utiliser un coeff fixe dans les play* functions (ex: `0.4`) et laisser `masterGain` gérer le volume user. ```js // Avant (dans chaque play*): gain.gain.setValueAtTime(volume * 0.4, ctx.currentTime); // Après: gain.gain.setValueAtTime(0.4, ctx.currentTime); ``` **2. PR description dit « State persisted in localStorage » — mais y'a ZERO code localStorage** Ni le toggle mute ni le volume ne sont sauvegardés. À chaque refresh, l'utilisateur perd ses prefs. Soit implémenter le localStorage, soit retirer la claim de la PR. ### 🟡 Minor **3. `playPlace()` est dead code** — défini mais jamais appelé dans app.js. Seul `playPlaceVariety()` est utilisé. Soit l'utiliser, soit le retirer pour éviter la confusion. **4. `lastState` variable dans `initSound`** — assigné mais jamais lu. Dead variable. **5. Volume slider sync** — Le slider HTML a `value="30"` et le JS default est `0.3`. Ça marche par coïncidence, mais si quelqu'un change le default dans le JS, les deux seront désynchronisés au load. Le JS devrait lire la valeur initiale du slider (ou vice-versa). --- Le design de l'IIFE module est propre, les sons procéduraux sont bien pensés. Fix le bug de volume et le localStorage, et c'est bon à merger. 🎵
ti-paul approved these changes 2026-05-15 00:51:49 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
les_clankeurs/pixel-clash!17
No description provided.