Interactive web application · 2026
PokéSynth
A synthesizer where every Pokémon is a unique musical instrument, driven by real game data.

PokéSynth — Case Study
Project Overview
PokéSynth is a browser-based synthesizer where every Pokémon becomes a unique musical instrument. Each Pokémon’s base stats and typing shape the sound it produces — from the warm sine waves of a Water-type to the harsh FM distortion of a Dragon-type. With 898 Pokémon available, the app generates 898 distinct instruments from real game data, with zero backend infrastructure.
Live: pokesynth.com
Problem & Motivation
Pokémon and music are two domains with massive, overlapping fan bases — yet few projects combine them in a meaningful, interactive way. The goal was to build a web toy where every Pokémon is playable: not just viewable, but sonically distinct and fun to experiment with.
The core design question: can six base stats and a type category produce enough timbral variety to make each Pokémon sound recognizably different?
Technical Approach
Synth Engine (synth-engine.ts)
The audio engine is built on Tone.js using a PolySynth wrapper. The Pokémon’s primary type selects one of 6 synthesis architectures:
| Category | Types | Inner Synth | Character |
|---|---|---|---|
fm |
Fire, Fighting, Dragon | FMSynth (sawtooth carrier) |
Aggressive, bright |
fm-eerie |
Ghost, Dark, Poison | FMSynth (high mod index, square mod) |
Haunting, unstable |
fm-heavy |
Rock, Ground | FMSynth (square carrier) |
Thick, percussive |
am |
Electric, Steel, Psychic | AMSynth |
Shimmering, metallic |
basic-sine |
Water, Ice, Flying | Synth (sine) |
Smooth, pure |
basic-tri |
Grass, Bug, Fairy, Normal | Synth (triangle) |
Warm, soft |
Each of the 6 base stats maps to a distinct audio parameter:
| Stat | Audio Parameter | Effect |
|---|---|---|
| HP | Envelope sustain & release | Higher HP → longer, more sustained tones |
| Attack | Distortion amount (+ FM modulation index) | Higher Attack → grittier, more aggressive timbre |
| Defense | Low-pass filter cutoff | Higher Defense → darker, more muffled sound |
| Sp. Atk | Reverb decay & wet mix | Higher Sp. Atk → more spacious, atmospheric |
| Sp. Def | Chorus depth & detune | Higher Sp. Def → wider, more shimmering |
| Speed | Envelope attack & decay speed | Higher Speed → snappier, more percussive onset |
The full effects chain runs: Synth → Filter → Distortion → Chorus → EQ3 → Compressor → Reverb → Limiter → Destination. Per-category EQ presets shape the final tone (e.g., boosted lows for Rock/Ground, scooped mids for Ghost/Dark).
PokéAPI Integration
All Pokémon data is fetched client-side from PokéAPI using TanStack Query with staleTime: Infinity — once fetched, data is cached for the session. The app provides three selection methods:
- Autocomplete search — fuzzy-matched against all 898 names, pre-fetched as a single lightweight list
- Paginated grid browser — browse all Pokémon with sprite thumbnails, using
keepPreviousDatafor seamless pagination - Random selection — pick a random ID from 1–898
Animated sprites are sourced from Generation V Black/White data when available, falling back to static front sprites.
Piano Keyboard
A 15-key polyphonic keyboard spanning just over one octave, with:
- Pointer events for unified mouse and touch input
- Keyboard mapping — keys A through L for white notes, W/E/T/Y/U for sharps
- Octave shifting — Z and X keys shift the playable range up and down
- Touch optimization:
contextmenusuppression andtouch-action: manipulationprevent unwanted browser behaviors on mobile
Dynamic Theming
When a Pokémon is selected, its primary type drives a real-time theme update via CSS custom properties:
--type-hue → base hue from type color map
--type-sat → saturation percentage
--primary → HSL value for primary UI color
--accent → hue-rotated (+40°) accent color
Every UI element — borders, backgrounds, text highlights, indicator LEDs — recolors instantly. The type color map covers all 18 Pokémon types with hand-tuned hue/saturation pairs.
Stat Visualizer
An animated bar chart displays all six stats with:
- Stat name and numeric value
- The mapped audio parameter label (e.g., “ATK → distortion”)
- A pulse animation triggered when notes are actively playing, providing visual feedback that connects the stats to the sound
UX Considerations
- CLS prevention — A fixed-height placeholder (
min-h-[280px]) reserves space for the Pokémon card and stat panel before any selection, eliminating layout shift - Mobile touch optimization — Context menu suppression,
touch-action: manipulation, and pointer events ensure smooth play on touchscreens - Responsive keyboard hints — Key labels shown on desktop, hidden on mobile where they’d be meaningless
- Retro aesthetic — Game Boy-inspired pixel art styling with a CRT scanline overlay, pixelated sprite rendering (
image-rendering: pixelated), and a monospace-adjacent type scale
Stack
| Layer | Technology |
|---|---|
| Framework | React 18 + TypeScript |
| Build | Vite |
| Styling | Tailwind CSS + CSS custom properties |
| Audio | Tone.js (Web Audio API) |
| Data fetching | TanStack Query v5 |
| API | PokéAPI (REST, no auth) |
| Routing | React Router v6 |
Outcome & Reflection
PokéSynth demonstrates that real game data can drive meaningful creative output. The six base stats provide enough dimensionality to make Charizard sound unmistakably different from Blastoise — not through arbitrary mapping, but because the stat distributions genuinely reflect each Pokémon’s character. A fast, frail Pokémon sounds fast and frail. A tanky defender sounds muffled and sustained.
The project runs entirely client-side with zero backend — all data comes from PokéAPI, all synthesis happens in the browser via Web Audio. This makes it instantly shareable, trivially deployable, and endlessly explorable across 898 unique instruments.
Gallery
Gallery coming soon
More visuals from this project are on the way.