Project Resonant · a field guide

How a machine hears a number.

This guide starts with air moving and ends with a physics simulation that can tell “three” from “eight”. Every figure on this page is computed live in your browser from real recordings.

Ten held-out clips from AudioMNIST · ~20 min read

01 · the problemSound is a terrible input format

Say “seven” out loud. Your vocal folds chop an airflow into pulses, your throat and mouth shape those pulses into resonances, and a pressure wave leaves your face at about 340 m/s. A microphone measures that pressure 16,000 times a second and writes down a number each time. One second of speech is a list of 16,000 numbers.

Pick a digit below and look at what a model is actually handed.

choose a recording—
loading…
Figure 1 · one second of speech, as recorded. Every clip on this page comes from AudioMNIST, an open corpus of spoken digits. This is amplitude over time, 16,000 samples of it. You can see where the energy is, and roughly how many syllables there are, but not which word it is. The same digit spoken by two people produces two very different squiggles, and shifting the recording by 5 milliseconds changes every number while changing nothing a listener would notice. A classifier trained directly on these numbers has to spend much of its capacity learning to ignore those differences.

The fix is much older than deep learning: instead of describing the wave, describe which frequencies are present, and when. Two recordings of “seven” differ enormously sample by sample but look almost the same once you plot them that way. Every architecture in the rest of this guide, conventional or not, starts from that same transformation.

02 · the fourier transformOne slice, taken apart into pure tones

Fourier showed that any wave, however jagged, can be written as a sum of plain sine waves with different frequencies, amplitudes and phase offsets. The Fast Fourier Transform (FFT) is the algorithm that finds those ingredients: it takes a block of samples and returns how much of each frequency is in it.

The catch is in the word block. An FFT over a whole second tells you every frequency that second contained but nothing about the order they arrived in, so “seven” and “teevn” would come out the same. Instead we analyze a short block: 512 samples here, or 32 milliseconds, short enough that the mouth barely moves.

drag the window—
Figure 2 · a 32 ms window and its spectrum. Left: the highlighted slice of the recording. Right: the FFT of that slice, showing energy against frequency from 0 to 8 kHz. Slide the window into a vowel and the low end fills with evenly spaced peaks (the pitch of the voice and its harmonics) under a lumpy envelope (the formants, the resonances of the throat and mouth that decide which vowel it is). Slide it onto the “s” of “seven” or the “x” of “six” and that structure flattens into a broad hiss weighted toward the top of the range. In the silence at either end the whole curve drops. This difference between periodic sound and noise does a lot of work later on.

03 · the STFTSlide the window: a picture of sound

The Short-Time Fourier Transform (STFT) is just the previous figure on repeat: take a window, FFT it, shift the window forward by a fixed hop, repeat to the end of the file, and stack the results side by side. The result is a spectrogram: time along the x-axis, frequency up the y-axis, brightness for energy.

Two parameters set the whole grid, and they trade against each other. A longer window resolves frequency more finely but smears events in time; a shorter one localizes the click of a “t” but cannot tell 300 Hz from 320 Hz. The settings used throughout this guide are the ones the digit experiments actually use: a 512-sample window (n_fft = 512, 32 ms) hopping 256 samples (16 ms), which gives 62.5 frames per second, one frame for every 16 ms of speech.

Before the STFT, each window is multiplied by a smooth bell (a Hann window). Cutting a block out with hard edges would add a click the FFT dutifully reports as high-frequency energy that is not in the signal; tapering the edges removes it.

STFT · 257 frequency bins × 61 framespower, log scale
Figure 3 · the spectrogram of the selected digit. Horizontal stripes are the harmonics of the voice, the wavering bright bands are formants moving as the mouth changes shape, and vertical smears are the bursts and hisses of consonants. Most audio models are built on this representation. It is also what let computer-vision architectures work on sound: a spectrogram is a picture, so anything that finds patterns in pictures can be pointed at it.

04 · the mel scaleRebuilding the axis around the ear

257 frequency bins, evenly spaced from 0 to 8 kHz, is not how hearing works. The gap between 100 Hz and 200 Hz is obvious to anyone; the gap between 6,000 Hz and 6,100 Hz is inaudible, though both are 100 Hz. The ear’s resolution is roughly logarithmic: fine at the bottom, coarse at the top.

The mel scale (from “melody”) is the warped frequency axis where equal distances sound like equal pitch steps. The common formula is mel = 2595 · log₁₀(1 + Hz / 700), near-linear below 1 kHz and increasingly compressive above it.

Press play below and watch which filters actually fire while the digit is spoken.

showing the clip average
hz → melthe ear’s ruler
the filterbank16 triangles over 257 bins
Figure 4 · warping, then pooling, lit up by the actual recording. Left: physical frequency in, perceptual frequency out. Each stem marks a mel band this clip is using right now, placed at its frequency and rising to where the ear puts it. Right: the filterbank from the project’s front end, 16 triangular windows spaced evenly on the mel axis, which makes them narrow and crowded at low frequencies and wide and sparse at high ones. Each one brightens with its own energy. Press play and you can watch a vowel light up the low, tightly packed filters while the “s” in “six” and “seven” puts energy into the wide ones at the top. Each triangle sums the STFT bins underneath it into a single number, so a column of 257 numbers becomes a column of 16.

There is one more step, and it matters more than it looks. We hear loudness on a log scale too, so we take the log of each band’s energy. That turns the ratios that matter into differences a network can add and subtract, and it stops one loud vowel from dominating the numbers. STFT → mel filterbank → log is the standard audio front end, and its output is the log-mel spectrogram.

log-mel · 16 bands × 61 frameswhat the model is fed
Figure 5 · the same second, now 976 numbers instead of 16,000. Low bands are at the bottom and high bands at the top. Almost everything that distinguishes one spoken digit from another survives this compression, which is why nearly every speech system, whether it classifies or synthesizes, starts here.

Sidebar: what is a “mel transcoder”?

A neural network that manufactures a mel spectrogram from something coarser. The motivating case is privacy: an always-on sensor in a hospital ward or a street can be built to record only very coarse acoustic energy, such as third-octave bands every 125 ms, from which speech cannot be reconstructed or overheard. That data is far too crude for a pretrained audio classifier, which expects standard mel frames every 10 ms. A mel transcoder is trained to upsample and warp the coarse measurement into the mel grid those classifiers already understand, usually with a teacher–student objective: it is rewarded for producing a spectrogram the downstream classifier reads correctly, not for matching pixels. In effect it translates between two feature resolutions, which is a useful reminder that in an audio stack the mel spectrogram is not just a processing stage but an interface.

05 · the standard stackAn encoder, and three ways out

With the front end fixed, the rest of a speech system has a common shape: an encoder that turns the mel frames into a sequence of learned feature vectors, and a decoder that converts those into whatever you want out. Almost every well-known model is a choice of encoder (CNN, RNN/LSTM, Conformer, Transformer, state-space model) crossed with a choice of decoder.

FRONT END (FIXED MATH)ENCODER (LEARNED)DECODERS (LEARNED)waveform16 kHzSTFT512 / 256mel + log16–80 bands62.5 fpsencoderCNN · LSTMConformerTransformer · SSMclassificationpool over time → linear → softmax→ “this clip is a 7”text (ASR)CTC or autoregressive decoder→ “seven”audio (TTS / voice conv.)mel head → vocoderHiFi-GAN · Vocos · iSTFT head→ a new waveformthe same features feed all three;only the head and the loss change
Figure 6 · the conventional pipeline. The front end is fixed arithmetic with no learned parameters. Everything to its right is trained, and what you train it on is set entirely by the decoder you attach.

What the training data has to be

GoalTraining pairsLoss
Classificationclip → one label ("7")cross-entropy
Speech → textclip → transcript, no per-frame alignment neededCTC, or teacher-forced cross-entropy
Text → speechtranscript → clip, same corpus read the other waymel regression + vocoder losses
Speech → speechsource clip → target clip (denoised, or another voice)reconstruction + multi-resolution STFT

Getting back out to audio: the phase problem

Classification and transcription end in a small output. Generating audio does not, and it runs into a problem worth understanding, because it explains why so much of the field exists.

The STFT produces complex numbers: a magnitude (how much of this frequency) and a phase (where in its cycle that frequency is at this instant). A spectrogram displays magnitude only; the mel filterbank then throws away even the fine frequency detail. The inverse STFT (iSTFT), an inverse FFT per frame followed by adding the overlapping frames back together, reconstructs the waveform perfectly, but only if you give it both parts. Hand it magnitudes with no phase and the overlapping frames fight each other instead of adding up, and you get a smeared, metallic buzz.

So a model that generates audio has to supply the missing phase. There have been three main approaches:

  • Griffin–Lim (algorithmic). Guess a phase, iSTFT, re-STFT, keep the target magnitudes and the newly implied phase, repeat. It converges to something playable but famously robotic. Where you have heard it: the original Tacotron (2017) used Griffin–Lim, which is why early neural TTS demos had that ringing, underwater sound, and librosa’s griffinlim is still the two-line baseline most people try first.
  • Neural vocoders (the modern default). A network trained on thousands of hours of real speech maps mel frames straight to samples. It never “solves” for phase; it has learned what real glottal pulses, real fricative noise, and real micro-jitter look like. Where you have heard them: WaveNet (2016) predicted one sample at a time, which sounded remarkable but was very slow, and Tacotron 2 used it as the vocoder behind a mel decoder; HiFi-GAN (2020) made GAN vocoding fast and clean enough to become the default, and it is the vocoder inside Kokoro and StyleTTS 2; BigVGAN scaled it to universal, any-voice audio; DiffWave and WaveGrad sculpt noise into a waveform by diffusion; and Vocos predicts STFT coefficients, magnitude and phase, then lets a single iSTFT do the synthesis, which makes it fast and a good compromise.
  • Multi-resolution spectral losses. Whatever the generator, training compares the output with the target through several STFTs at once: short windows catch clicks and transients, and long windows catch pitch and vowels. Where it comes from: Parallel WaveGAN (2019) introduced the multi-resolution STFT loss that nearly every vocoder since has adopted, including HiFi-GAN’s mel loss and SoundStream/EnCodec’s spectral reconstruction terms; the differentiable-DSP line (DDSP) leans on the same idea.

Text-to-speech is not simply speech-to-text backwards

Its structure mirrors recognition (text encoder → acoustic decoder → vocoder → waveform), but its difficulty does not. Recognition is many-to-one: a thousand different performances of “seven” must collapse onto one string, so the model’s job is to throw variation away. Synthesis is one-to-many: from five letters the model must invent a speaker, a pitch contour, a speaking rate, the emphasis, and the breaths. Nothing in the input says which of the infinitely many correct readings to produce, which is why modern TTS spends its parameters on duration predictors, style/speaker embeddings, and generative decoders.

06 · a different coreWhat if the middle box were physics?

Everything above treats the encoder as a stack of matrix multiplications. Here is the alternative Project Resonant is built to test: replace it with a population of coupled oscillators, and let the audio drive them.

The motivation is practical: speech is oscillation, from vocal folds cycling and resonances ringing to syllables arriving 4 to 8 times a second. A model whose internal state is also a set of phases and frequencies starts closer to its data than a general-purpose stack of matrices does. The whole history of the signal also lives in a fixed-size state, the phases, instead of a context window that grows over time.

The stadium crowd

Imagine a stadium at a concert, everyone holding their phone with the light on, each person swinging it in a slow windmill while their favourite singer plays.

A concert stadium seen from outside and above, roof open, a lit stage at the far end, every seat drawn as a phone light coloured by its phase, with a bright wave sweeping around the near side
Figure 7 · the whole model, in one picture. Every seat is an oscillator and every colour is where that phone’s light is pointing. The bright band sweeping the near side is the wave, and the stage at the far end is the audio driving it. Nobody moves and the seating chart never changes. What travels is the timing between neighbours, which is what the coupling term computes and what the readout measures.
  • Where each person’s light is pointing right now is that oscillator’s phase, θ. It swings around and around; straight up at 0 and at 2π is the same place in the circle.
  • Everyone has a preferred swing tempo, ω: the speed they would settle into if nobody else were there.
  • People glance at their neighbours and adjust: the coupling. You speed up or slow down depending on whether the lights beside you are ahead of yours or behind it.
  • The music from the stage pushes the whole section on the beat: the drive. This is where the audio comes in: a loud low note pushes on one part of the crowd, a bright cymbal on another.
  • A gentle tendency to let your arm drop keeps the whole thing from running away: the pinning.
  • The seating chart never changes. Nobody swaps seats; only the wave of light moves through them.

Written down, this is the Kuramoto model, one of the best-known models of synchronization in physics. It describes how fireflies end up flashing together and how metronomes on a shared board fall into step. Each oscillator updates its phase like this:

dθi/dt = ωi + Σj K(i−j) · sin(θj − θi) − λ · sin(θi) + Fi(t)
ωOwn tempo. Every oscillator has a natural frequency it would keep on its own. In this demo they are laid out deliberately: each row of the grid is tuned to one band of the speech envelope, from about 0.4 Hz at the bottom to 6 Hz at the top.
K, sin(Δθ)Peers. The pull toward agreement, and the only place the model can learn structure. It depends on the phase difference, so it describes a relationship rather than a value, and that is what lets the population synchronize instead of simply adding up.
λPinning. A pull toward a rest phase. Without it the system is marginally stable and gradients through long sequences explode; with it, memory of the past decays at a controllable rate.
F(t)The audio. Each mel band’s loudness at this instant, fed into its own row of oscillators. A louder band pushes its row harder.
Figure 8 · the whole core, in one line. Compared with a transformer layer, there is no attention matrix, feed-forward block or layer norm, just a tempo, a rule for neighbours, a brake and an input.

Coupling strength is the most interesting setting. If it is too weak, every oscillator ignores the others and the field is just a set of independent filters. If it is too strong, the whole population locks into one rigid clump that reports little more than “loud”. The useful range is in between, where parts of the field synchronize and parts do not, and which parts do depends on what it is hearing.

toy: 24 oscillators on a ringK = 0.00 · R = —
Figure 9 · synchronization, from nothing to total. 24 oscillators with different natural tempos, drawn as dots on their shared circle. At K = 0 they smear around it forever. Push K up and they gather into a clump. The arrow is the order parameter R: the average of all the phases treated as unit vectors. R near 0 means scattered and R near 1 means locked together. The console in section 07 plots R for each channel as the clip plays.

Why a torus

The oscillators are not a loose bag; they sit on a 16 × 16 grid, and coupling depends only on the offset between two cells, not on their absolute position. The same neighbourhood rule applies everywhere, which is what makes it a convolution. Rows are frequency bands (mel band b drives grid row b, so the grid inherits the ear’s layout); columns are a second dimension the dynamics can spread into.

Both axes wrap: the last row’s neighbour is the first row, the last column’s neighbour is the first column. A grid with both edges glued is a torus. This is worth doing for two reasons. First, with no edges there are no special cases: every oscillator has the same neighbourhood, so one small kernel describes the whole field. Second, a translation-invariant kernel on a periodic grid is a circular convolution, which becomes a pointwise multiplication in Fourier space, so coupling all 256 oscillators to all 256 others costs one FFT rather than a 256 × 256 matrix. The physics of the configuration on this page is about 2,000 numbers.

A torus drawn as a lattice of dots, each coloured by its phase, with a travelling wave wrapping around both the ring and the tube
Figure 10 · the field on the shape it lives on. The same phase colours as the crowd, now wrapped onto the surface the grid actually forms. Rows run around the tube, which is the frequency axis from low bands to high, and columns run around the ring. Follow any row or column far enough and you arrive back where you started. Because of that, no oscillator sits on an edge, one small kernel describes every neighbourhood in the field, and the whole coupling step becomes a single FFT.

Reading a physical system

You cannot feed phases to a classifier directly, because θ = 0.01 and θ = 6.27 are neighbours on the circle but look far apart as numbers. So the readout takes sin θ and cos θ, which are smooth around the wrap, and summarizes each oscillator over the clip with four numbers: mean sin and mean cos (where it sat) and the mean sin and cos of its per-frame phase step (how fast it turned; an oscillator locked to the stimulus reports the stimulus’s rhythm, and a free one reports its own). With 4 channels × 256 cells that is 4,096 features, and a plain linear layer on top.

IDENTICAL FRONT ENDPHYSICS CORE (REPLACES THE ENCODER)SAME HEADSSTFT + mel16 bandsdrive mapband b → row b× gain4 × (16 × 16) oscillatorsθ ← θ + dt · (ω + coupling − pinning + drive)coupling = circular convolution (one FFT)periodic in both axes = a torusstate θ carries over to the next frame:a fixed-size streaming memoryreadoutsin θ, cos θ+ phase stepssoftmaxdigit 0–9CTCtextmel head→ vocoderphases persist from frame to frame: the recurrence
Figure 11 · the same stack with a different middle. The front end and the output heads are deliberately standard choices, shared with the conventional models used as controls, so that any difference in results comes from the core and not from the plumbing around it.

07 · watch it runThe whole pipeline, live

Below, everything on this page runs end to end. Pick a digit and press play: the clip is analyzed into mel bands, the bands are injected into the oscillator field, 61 frames of physics are integrated, and the linear readout reports what it thinks it heard, all in your browser in about the time it takes to blink. Or press record and say a digit yourself, and your own voice goes through the same path.

Before you look, note that the physics here is not trained. The coupling kernel is a random draw that gradient descent never touched, the natural frequencies were set by hand, and the only fitted part is the final linear layer. On held-out speakers this configuration gets — of ten-way digits right. That says something about how much structure raw oscillator dynamics give their input, and section 08 runs the control that shows how much of it comes from the coupling. It is not a claim that any of this beats a trained network.

waveform
STFT
mel bands
drive
oscillator field
readout
the field on its toruschannel 1 of 4
turn the dialsas fitted
or record yourself saying a digit
input0.00 s
order parameter R · drive rows now—
readout—
source—
detected—
mean R—
sim time—
all four channels, flattenedhue = phase
Figure 12 · the live console. Three things are worth watching. During silence the field still turns, each row at its own tempo, slow at the bottom and quick at the top. When the word arrives, the driven rows lurch, the pattern reorganizes and the order-parameter traces swing. And the readout usually settles on an answer well before the clip ends, then holds it. Every dial changes the real physics, but the readout was fitted at one setting and is never refitted, so moving a dial hands it a system it has never seen. That is what the badge means, and it is why predictions fall apart quickly. It does not tell you how much the coupling was contributing; the next section measures that. About the record button: your recording is resampled to 16 kHz, high-passed, trimmed to the word, and normalized to the corpus’s level and one-second window before it is analyzed. The front end’s levels are fixed, so a raw microphone signal would be a different kind of input, not just a louder one. Expect it to do noticeably worse on your voice than on the clips: you have a different microphone, a different room, and a linear readout fitted on 48 speakers who are not you.

08 · this is just the beginningWhat this shows, and what it doesn’t

Spoken digits are an easy task with a long history. They are the “hello world” of speech, and a physical spin-torque oscillator reached 99.6% on a comparable benchmark back in 2017. A high number here is not evidence that oscillator cores beat transformers at speech. What the demo does show is this:

  • Untrained dynamics carry usable structure. No gradient touched the kernel. The audio drives the field, the field organizes, and a linear probe reads the digit off the result.
  • The state is fixed-size and streaming. 4 × 256 phases at every frame, whatever the length of the input, with no cache that grows.
  • The whole model is tiny. The physics is roughly 2,000 numbers, and it simulates a second of audio in a browser tab in a few dozen milliseconds.

How much of it is the synchronization?

There are really two different machines in that description, and it helps to separate them. One is a bank of independent resonators: 1,024 oscillators, each tuned to a frequency band, each shoved by its own slice of the audio, none of them aware of any other. That is a perfectly good feature extractor, but not a new idea, since it is what a filterbank does. The other is the coupled field: the same oscillators, now allowed to pull on their neighbours, so what any one of them does depends on what the ones around it are doing. Only the second one is a synchronization model.

The only difference between them is the coupling kernel. Set it to zero and the neighbours stop listening to each other, while everything else stays the same. Fit the same kind of linear readout on each and you can measure what the coupling adds:

FieldReadoutTest accuracy
coupled: neighbours interactfitted on this field96.2%
uncoupled (K = 0): a plain resonator bankrefitted on this field94.2%
uncoupled (K = 0)the coupled field’s readout, unchanged17.4%

On spoken digits, letting the oscillators talk to each other is worth about two points over letting them ring independently. The gain is real and repeatable across seeds, but much smaller than the headline number suggests: most of the work is done by the resonator bank and a generous linear readout.

The third row is easy to misread. It does not measure the coupling. It shows what happens when you change the system but keep the old readout: the linear layer was fitted to one physical system and handed a different one, so it fails, much as a key fails in a lock it was not cut for. The uncoupled field itself works fine, as the second row shows; the old readout simply no longer matches it.

A two-point gap still matters because of what is being compared. A resonator bank is a fixed function: each oscillator's response is decided the moment you choose its frequency. A coupled field has a shape that can be changed. The kernel says who listens to whom and how strongly, and that shape is what makes patterns like travelling waves and partial synchronization possible. Here the shape is a random draw that no gradient ever touched, and it still adds two points. The more interesting question is what a good coupling would do.

There are two obvious ways to find out, and both are still open. Train it: the kernel is a small, differentiable set of parameters, so gradient descent can shape who couples to whom instead of leaving it to chance. Change the physics: Kuramoto's sin(θⱼ − θᵢ) is only the simplest way for two oscillators to interact. Adding a phase lag gives travelling waves, adding a second harmonic splits the population into clusters instead of one clump, and oscillators that carry an amplitude can express loudness as well as timing. Each is a different coupling law over the same field, and each is being tested the same way as everything else here, against a control model of the same size.

Where this goes

The open question is whether building the physics of waves into a model makes it a better architecture for input and output that are themselves waves, such as real-time speech in and out of one core that can be interrupted and keeps its context, or whether a plain stack of matrices beats it at every size. The research is set up so that the idea can fail cleanly, with parameter-matched controls, fixed baselines and a record of every run. That work is ongoing.

09 · go deeperThe code, and the prior art

Everything on this page, from the front end and the oscillator core to the readout and the ten recordings, comes from an ongoing research project. Its repository holds the hypothesis, the architecture, every experiment log, and the results that went against my own expectations. The papers, listed on my research page, are public, and each one comes with its code and the raw data behind every number it quotes, so anything it claims can be checked, reproduced or disproved.

The research on GitHub →

Prior art worth reading