Pokémon Synthesizer · 2026
PokéSynth
A synthesizer where every Pokémon plays as its own instrument, tuned to its real stats.

PokéSynth
Project Overview
PokéSynth is a synthesizer where every Pokémon is its own instrument. A Pokémon’s base stats and typing shape the sound it makes, from the warm sine tones of a Water-type to the harsh FM bite of a Dragon-type. There are 898 Pokémon in play, which means 898 distinct instruments, all generated from real game data with nothing running on a server.
Live: pokesynth.com
Problem & Motivation
Pokémon and music both have huge, overlapping fan bases, and almost nobody had put them together in a way you can actually play with. I wanted a web toy where every Pokémon is playable: not just something you look at, but something that sounds distinct and is fun to mess around with.
The question underneath it: can six base stats and a type produce enough variety in tone to make each Pokémon sound recognisably different?
Technical Approach
Synth Engine (synth-engine.ts)
The audio engine runs on Tone.js wrapped in a PolySynth. A Pokémon’s primary type picks 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 signal path runs Synth → Filter → Distortion → Chorus → EQ3 → Compressor → Reverb → Limiter → Destination. Per-category EQ presets shape the final tone: boosted lows for Rock and Ground, scooped mids for Ghost and Dark.
PokéAPI Integration
All Pokémon data is fetched in the browser from PokéAPI using TanStack Query with staleTime: Infinity, so once something is fetched it stays cached for the session. There are three ways to pick a Pokémon:
- Autocomplete search, fuzzy-matched against all 898 names, pre-fetched as one lightweight list
- A paginated grid you can browse with sprite thumbnails, using
keepPreviousDataso pagination doesn’t flicker - Random selection, which just grabs an ID between 1 and 898
Animated sprites come from Generation V Black/White data where it exists, falling back to the static front sprite otherwise.
Piano Keyboard
A 15-key polyphonic keyboard covering just over an octave:
- Pointer events so mouse and touch are handled the same way
- Keyboard mapping: A through L for the white notes, W/E/T/Y/U for the sharps
- Octave shifting on Z and X to move the playable range up and down
- Touch handling that suppresses the context menu and sets
touch-action: manipulationso mobile doesn’t fire off unwanted browser gestures mid-play
Dynamic Theming
Pick a Pokémon and its primary type retints the whole interface in real time through 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 element recolours at once: borders, backgrounds, text highlights, even the indicator LEDs. The type colour map covers all 18 types with hand-tuned hue and saturation pairs.
Stat Visualiser
An animated bar chart shows all six stats, each with:
- The stat name and its numeric value
- The audio parameter it maps to (for example “ATK → distortion”)
- A pulse animation that fires while notes are playing, so you can see the stats driving the sound
UX Considerations
- No layout shift: a fixed-height placeholder (
min-h-[280px]) holds space for the Pokémon card and stat panel before you’ve selected anything, so nothing jumps around - Mobile touch: context menu suppression,
touch-action: manipulation, and pointer events keep play smooth on a touchscreen - Responsive hints: key labels show on desktop and hide on mobile, where they’d mean nothing
- Retro look: Game Boy pixel-art styling with a CRT scanline overlay, pixelated sprite rendering (
image-rendering: pixelated), and a near-monospace 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 is proof that real game data can drive something genuinely creative. Six base stats give you enough to work with that Charizard sounds unmistakably different from Blastoise, and not because I arbitrarily made it so. The stat spreads genuinely reflect each Pokémon’s character, so a fast, frail one sounds fast and frail, and a tanky defender sounds muffled and sustained.
The whole thing runs client-side with no backend at all: the data comes from PokéAPI, the synthesis happens in the browser through Web Audio. That makes it instantly shareable, trivial to deploy, and endlessly explorable across all 898 instruments.
Gallery
Gallery coming soon
More visuals from this project are on the way.