SoundBoard
A web-based sound synthesizer and board management application built with React and the Web Audio API. Users can create multiple custom soundboards, synthesize sounds using oscillators with ADSR envelopes, record audio from their microphone, and trigger sounds via keyboard shortcuts or a 3x3 grid.
Key Decisions
| Decision | Rationale | Outcome |
|---|---|---|
| Synthesized sounds over audio files | Eliminates asset management, enables real-time customization | Good — flexible and lightweight |
| Singleton AudioService pattern | Single AudioContext, centralized buffer/node management | Good — efficient resource usage |
| ADSR envelope system | Professional sound shaping, matches user expectations | Good — satisfying sound quality |
| Keyboard bindings 1-9 | Intuitive mapping, matches 3x3 grid layout | Good — fast user interactions |
| Web Audio API only | No external dependencies, full browser control | Good — no dependency bloat |
| React hooks for state | useAudio and useKeyboard separation of concerns | Good — maintainable and testable logic |
| Multiple boards with CRUD | Users can create, rename, and delete sound collections | Good — scalable organization |
| SoundEditor for real-time tweaking | Sliders for frequency, waveform, duration, and ADSR | Good — interactive sound design |
| RecordingService for mic input | MediaRecorder API with MIME type fallback chain | Good — robust recording across browsers |
| Long press gesture for edit mode | useLongPress hook with progress feedback (2s threshold) | Good — clean mobile-friendly UX |
| File upload for custom sounds | AddSoundButton with hidden file input pattern | Good — flexible content sourcing |
| ErrorBoundary for crash resilience | Class component error boundary with reset capability | Good — improved app stability |
Patterns Established
| Pattern | Description | Reusable |
|---|---|---|
| AudioService singleton | Centralized Web Audio context management | Yes |
| SoundGenerator with presets | Oscillator synthesis with ADSR envelopes | Yes |
| useAudio hook | Playback state management for React | Yes |
| useKeyboard hook | Key binding to actions with repeat prevention | Yes |
| CSS co-location | Component.tsx paired with Component.css | Yes |
| RecordingService singleton | MediaRecorder with MIME negotiation | Yes |
| useLongPress hook | Configurable threshold with progress state | Yes |
| SliderControl component | Reusable range input with label/value display | Yes |
| Board management pattern | CRUD operations for named collections | Yes |
| fetchWithRetry utility | Exponential backoff for HTTP requests | Yes |
Lessons Learned
| Lesson | Context | Impact |
|---|---|---|
| Browser autoplay policy requires user gesture | Calling resumeContext() on first click | Prevents audio initialization failures |
| Web Audio rate/volume can’t change mid-play | Need to stop and restart sound to apply changes | Limits real-time modulation without extra complexity |
| Oscillator presets are more flexible than files | Customization and sound design are enabled at the code level | Dramatically reduced app size |
| MediaRecorder MIME type browser variance | Must detect supported types at runtime via fallback chain | Reliable recording on Chrome, Safari, and Firefox |
| Long press needs both mouse and touch | Desktop vs mobile input divergence | Consistent gesture behavior across devices |
Technical Debt
| Item | Priority | Notes |
|---|---|---|
| No persistence for custom sounds | Medium | Board data is currently in-memory only; planned for v0.3 |
| Dual type files | Low | Types exist in both types.ts and types/index.ts; consolidate |
| Volume slider UI-only for SystemSounds | Low | AudioToolbox ignores app volume; migration to AVAudioPlayer needed |