van den Oord, Dieleman, Zen et al. (DeepMind), 2016

WaveNet: A Generative Model
for Raw Audio

Generate sound one sample at a time — 16,000 predictions per second — using a stack of dilated causal convolutions that reach back thousands of timesteps without a single recurrent connection.

Prerequisites: Convolutions + Autoregressive models
11
Chapters
6
Simulations

Chapter 0: The Problem

Hold a microphone up to a voice. Sample the air pressure 16,000 times every second. That stream of numbers is the sound. One second of CD-quality speech is 16,000 to 44,100 numbers in a row — and to generate speech, you have to produce every single one of them, in order, and have them line up into something a human ear accepts as a voice.

In 2016, no one generated audio this way. Text-to-speech systems either concatenated pre-recorded snippets of human speech (unit selection) or ran a hand-built vocoder — a signal-processing pipeline that turned predicted spectral features back into a waveform. Both sounded robotic. The waveform itself was never modeled directly; it was assembled or synthesized by formula.

The core question: Can a single neural network learn the distribution of raw audio waveforms — predicting each of the 16,000-per-second samples directly — well enough to produce speech a human rates as more natural than the best existing systems?

Why this is brutally hard

Pixels in an image have a comfortable resolution: a 256×256 image is ~65,000 values, and a pixel two rows up is "close." Audio is different in one terrifying way: temporal resolution. A meaningful chunk of speech — a single phoneme like "ah" — spans roughly 50 milliseconds. At 16 kHz, that is 800 samples. A word spans tens of thousands of samples.

So if you want a model that captures even a single phoneme of context, it must look back hundreds of samples. To capture word-level structure, thousands. This is the central tension of the whole paper: audio needs a huge receptive field — the number of past samples the model can "see" when predicting the next one.

A worked sense of scale

Let's make the numbers concrete. Suppose we want the model to remember 1 second of audio when predicting the next sample:

QuantityValueWhy it hurts
Sample rate16,000 Hz16,000 predictions per second of output
1 phoneme of context~800 samplesMinimum to capture a single speech sound
1 second of context16,000 samplesNeeded for prosody, word boundaries
Naive conv (filter 2) layers for 16k reach15,999 layersReceptive field grows by +1 per layer — hopeless

That last row is the whole problem in one line. A plain stack of small convolutions grows its receptive field linearly with depth: each layer adds one sample of reach. To see 16,000 samples back you would need ~16,000 layers. That network would never train. WaveNet's job is to get that same reach with ~30 layers.

Why not just use an RNN?

An LSTM can, in principle, carry information across thousands of timesteps through its hidden state. So why not use one? Two reasons the paper cares about:

So the bet of the paper is: replace recurrence with a clever convolutional architecture that achieves enormous receptive fields cheaply. Everything that follows — causal convolutions, dilation, gating, residuals — is in service of that one bet.

Why is generating raw audio harder than generating, say, a 256×256 image?

Chapter 1: The Key Insight

WaveNet factorizes the joint probability of a waveform x = {x1, ..., xT} into a product of conditionals, exactly the way a language model factorizes a sentence into next-token probabilities:

p(x) = ∏t=1..T p(xt | x1, ..., xt−1)

Read this carefully. Every audio sample xt is predicted from all the samples before it. The model is autoregressive: it outputs a probability distribution over the next sample, you draw one, append it, and repeat. Exactly like GPT predicting the next word — except the "words" are 16,000 sound samples per second.

The insight: Implement that conditional distribution with a stack of dilated causal convolutions. "Causal" guarantees you never peek at the future (you can't, you haven't generated it yet). "Dilated" lets the receptive field grow exponentially with depth, so a few dozen layers reach back thousands of samples — no recurrence required.

The two-word fix that makes it work

Each word in "dilated causal convolution" solves one specific failure of the naive approach:

causal
The prediction at time t may only depend on samples ≤ t. No leaking the future into the present.
dilated
Skip input samples with a growing gap, so the receptive field doubles each layer instead of adding 1.
convolution
Shared weights, fully parallel at train time, short gradient paths — none of the RNN pain.

What the whole model is, in one breath

A WaveNet is: input waveform → a causal convolution → a tall stack of residual blocks, each containing a dilated convolution with a gated activation → outputs from every block summed via skip connections → two 1×1 convolutions with ReLUs → a 256-way softmax over the next sample's value. We will build every one of those pieces from scratch in the chapters ahead, and you will see exactly what tensor flows where.

How does WaveNet model the probability of an entire waveform?

Chapter 2: Background — Audio as a Sequence

Before the architecture, we need to be precise about the data. WaveNet operates on PCM (pulse-code modulation) audio: the raw output of an analog-to-digital converter. Two numbers define it.

The data flow we are modeling

Let's trace the shapes, because "what tensor goes in, what comes out" is the whole game:

StageTensorMeaning
Raw clip (1 sec, 16 kHz)[16000]16-bit integers, the ground-truth waveform
After µ-law quantize (Ch 5)[16000]integers in [0, 255] — one of 256 categories
One-hot / embedded input[16000, 256] or [16000, C]fed into the causal conv stack
Network output per timestep[16000, 256]softmax distribution over the next sample's category

Why a softmax over 256 categories, not a regression?

Your first instinct is to predict a real number (the next sample value) and minimize squared error. WaveNet deliberately does not. Instead it outputs a categorical distribution — a softmax over discrete sample values — and is trained with cross-entropy, maximizing the log-likelihood of the true next sample.

Why categorical beats regression here: A Gaussian (which is what squared-error implicitly assumes) is unimodal and symmetric. But "the next audio sample given this context" can be genuinely multimodal — at the onset of a vowel the next sample could plausibly jump up or down. A softmax makes no assumption about the distribution's shape; it can represent any spiky, multi-peaked shape over the 256 values. The PixelRNN authors found the same thing for image pixels.

But there's a catch: a softmax over 16-bit audio needs 65,536 output units per timestep. That's an enormous, mostly-empty output layer. Chapter 5 is entirely about a transformation that crushes those 65,536 values down to a tractable 256 — without the ear noticing.

Train time vs. generate time — the asymmetry

This asymmetry is easy to miss and crucial to understand:

Why does WaveNet model each sample with a 256-way softmax instead of regressing a single real value?

Chapter 3: Causal Convolutions

The first ingredient. A causal convolution is an ordinary 1-D convolution with one rule enforced: the output at time t may only depend on inputs at times t, t−1, t−2, ... — never t+1 or later. The model is forbidden from peeking at the future.

This is not optional decoration; it's what makes the autoregressive factorization from Chapter 1 true. If output t could see input t+1, then p(xt|x<t) would secretly depend on a future sample, and the product-of-conditionals would be a lie.

How you actually implement causality

For 1-D data the trick is delightfully simple: take a normal convolution and shift the output. Pad the input on the left with (filter_size − 1) zeros and use no padding on the right. Then output position t mechanically only overlaps inputs up to t. (For images, the 2-D equivalent is a masked convolution — you zero out the filter weights that would touch future pixels.)

Causal Convolution — Plain Stack (filter size 2)
2 layers → receptive field = 3 samples

Click "Add Layer" and watch the highlighted output's receptive field (the input samples it can see) grow. Notice it grows by exactly +1 sample per layer. This is the painfully slow linear growth WaveNet must escape.

The receptive-field formula (worked)

For a plain stack of causal convolutions, the receptive field is:

R = (filter_size − 1) × (#layers) + 1

Walk through it. With filter size 2 and one layer, output t sees inputs {t−1, t} → R = (2−1)×1 + 1 = 2. Add a second layer: each of those two becomes a window of two, but they overlap, netting {t−2, t−1, t} → R = (2−1)×2 + 1 = 3. The paper's Figure 2 has 4 layers of filter-2 convs, giving R = 1×4 + 1 = 5. As foreshadowed: to reach 16,000 you'd need ~16,000 layers. Linear growth is the enemy.

The bottleneck, stated plainly: Causality is free and necessary. But causal convolutions alone buy receptive field one sample at a time. We need the same causality with exponential reach. That is dilation — the next chapter.
A plain causal-conv stack with filter size 2 and 8 layers has what receptive field?

Chapter 4: Dilated Convolutions (the showcase)

A dilated convolution (a.k.a. à trous, "with holes") is a convolution whose filter is applied over a region larger than its length by skipping input values with a fixed step, the dilation factor d. With d=1 it's a normal convolution. With d=2, a filter of size 2 looks at inputs {t, t−2}, skipping t−1. With d=4, at {t, t−4}.

The genius: stack layers with exponentially increasing dilation — 1, 2, 4, 8, ..., 512 — and the receptive field doubles every layer while every input sample is still visited (no gaps in coverage). You get exponential reach at the same per-layer cost as a normal convolution.

Why dilation works without holes in coverage: Layer with d=1 connects every adjacent pair. The next layer (d=2) skips one input — but that skipped input was already mixed into its neighbors by the d=1 layer below. Stacking 1,2,4,8 means every input sample contributes to the output through some path. You get the reach of a 16-wide filter with the parameters of a 2-wide one.
Dilated Causal Convolution Stack — Build It Yourself

Drag Layers to add dilated layers (dilation doubles: 1, 2, 4, 8, ...). Watch the highlighted output's receptive field — the blue lines — fan out across the input. Hit Animate to push a sample through the whole stack and see causality + exponential reach at once.

The receptive-field arithmetic — worked in full

For one block of dilations 1, 2, 4, ..., 2L−1 with filter size 2, the receptive field is:

R = 2L  (for filter size 2)

Derivation: each layer of dilation d and filter size 2 adds d samples of reach. Summing the dilations of one block:

1 + 2 + 4 + ... + 2L−1 + 1 = (2L − 1) + 1 = 2L

So a single block of 10 layers (dilations 1→512) gives a receptive field of 210 = 1024 samples. The paper notes this block "can be seen as a more efficient and discriminative counterpart of a 1×1024 convolution." Compare to the plain causal stack of Chapter 3: to reach 1024 you needed 1023 layers; here, 10.

Stacking blocks for even more reach

WaveNet repeats the dilation pattern: 1,2,...,512, 1,2,...,512, 1,2,...,512. Why reset to 1 instead of continuing to 1024, 2048? Two reasons: (1) stacking blocks keeps the receptive field growing while also increasing model capacity/non-linearity; (2) resetting to small dilations re-densifies the local coverage. Three blocks of 10 layers each gives roughly 3 × 1024 = ~3072 samples of receptive field — about 190 ms at 16 kHz, enough for the 2–3 phonemes the paper reports.

ArchitectureLayers for R = 1024Growth
Plain causal (filter 2)~1023linear (+1/layer)
Dilated causal (filter 2)10exponential (×2/layer)

The core insight as runnable code

python
# A causal dilated 1-D convolution, from scratch
import torch, torch.nn.functional as F

def causal_dilated_conv1d(x, weight, dilation):
    """x: [B, C_in, T]   weight: [C_out, C_in, K]"""
    K = weight.shape[-1]
    # left-pad so output[t] only sees inputs <= t (causality)
    pad = (K - 1) * dilation
    x = F.pad(x, (pad, 0))           # pad LEFT only
    return F.conv1d(x, weight, dilation=dilation)

# receptive field of a stack of these
def receptive_field(dilations, K=2):
    return 1 + sum((K - 1) * d for d in dilations)

block = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
print(receptive_field(block))          # 1024
print(receptive_field(block * 3))      # 3070  (~190 ms @ 16kHz)
A single block of dilations 1,2,4,...,512 (filter size 2) gives a receptive field of:

Chapter 5: µ-law Companding

Back to the problem we flagged in Chapter 2: 16-bit audio has 65,536 possible values per sample. A softmax over 65,536 categories is huge and wasteful. We want to collapse it to 256 categories. But naive rounding — just chop the 16 bits to 8 — sounds awful. Why?

Because loudness perception is logarithmic. Your ear is exquisitely sensitive to small changes among quiet sounds and relatively insensitive to small changes among loud sounds. Linear quantization wastes precious levels on loud samples (where you can't hear the difference) and starves quiet samples (where you can). The result is audible quantization noise in the quiet parts — exactly where speech detail lives.

The fix: Warp the amplitude axis before quantizing, using a logarithmic curve that allocates more levels to quiet sounds. This is µ-law companding (compress + expand), a 1972 telephony standard. Quantize in the warped space; the 256 levels land where the ear cares.

The µ-law transform

For an input sample xt normalized to −1 < xt < 1, with μ = 255:

f(xt) = sign(xt) · ln(1 + μ|xt|) / ln(1 + μ)

Symbol by symbol, with intuition:

After applying f, you uniformly bin the warped value into 256 levels. The paper reports this "produces a significantly better reconstruction than a simple linear quantization scheme" — and for speech, the reconstructed signal "sounded very similar to the original."

A fully worked numerical example

Take a quiet sample, x = 0.01:

f(0.01) = sign(0.01) · ln(1 + 255·0.01) / ln(256)
= ln(3.55) / ln(256) = 1.267 / 5.545 = 0.2285

A linear scheme would map x=0.01 to bin 0.01 (basically silence, indistinguishable from x=0). µ-law lifts it to 0.2285 — pushed well away from zero so it lands in its own distinct quantization bin. Now a loud sample, x = 0.9:

f(0.9) = ln(1 + 255·0.9) / ln(256) = ln(230.5) / ln(256) = 5.440 / 5.545 = 0.9810

The loud sample barely moves (0.9 → 0.981), so loud sounds get fewer levels — but the ear doesn't care. The quiet sample got 23× the relative resolution. That redistribution is the entire point.

µ-law Curve & Quantization — Drag μ

The diagonal gray line is linear quantization (equal-width bins). The orange curve is µ-law. The horizontal ticks show where the 256 levels land on the input axis after the warp: notice how they cluster near zero (quiet) when µ is large. Toggle off to compare against linear.

python
import numpy as np

def mu_law_encode(x, mu=255):
    """x in [-1, 1] -> integer category in [0, 255]"""
    f = np.sign(x) * np.log1p(mu * np.abs(x)) / np.log1p(mu)
    return ((f + 1) / 2 * mu + 0.5).astype(int)   # to [0,255]

def mu_law_decode(y, mu=255):
    """inverse: category -> waveform value"""
    f = 2 * (y / mu) - 1
    return np.sign(f) * (1 / mu) * ((1 + mu) ** np.abs(f) - 1)

print(mu_law_encode(np.array([0.01, 0.9])))   # [157 252]  -> distinct bins
Why does WaveNet apply µ-law companding before quantizing to 256 levels?

Chapter 6: Gated Activation Units

Inside each layer, after the dilated convolution, WaveNet does not use a ReLU. It uses a gated activation unit, borrowed from the gated PixelCNN:

z = tanh(Wf,k ∗ x)  ⊙  σ(Wg,k ∗ x)

Decode it:

Think of it this way: tanh proposes a signal; sigmoid decides whether to open or close the gate on it. If the gate is 0, nothing passes (this feature is irrelevant right now). If the gate is 1, the full tanh signal flows. It's a learned, per-element, per-timestep volume knob — closely related to the gates inside an LSTM, which is why it captures audio dynamics better than a plain ReLU. The authors report it "worked significantly better than the rectified linear activation function for modeling audio signals."

Data flow through one gated unit (with shapes)

One dilated layer takes the residual stream — say [B, C, T] with C = residual channels (the paper uses on the order of dozens) — and runs it through a single dilated conv that outputs 2C channels. That 2C is then split in half: the first C go to tanh (filter), the second C go to sigmoid (gate). Their elementwise product is [B, C, T] again — same shape in, same shape out, which is exactly what residual connections need.

Gated Activation — tanh content × sigmoid gate

Drag the gate's pre-activation. The blue curve is the tanh content; the orange is the sigmoid gate (0→1); the green is their product — what actually flows downstream. Push the gate negative and watch the green output get squeezed to zero regardless of the content.

python
def gated_unit(x, conv_2c):
    """conv_2c: dilated conv with 2C output channels"""
    h = conv_2c(x)                          # [B, 2C, T]
    f, g = h.chunk(2, dim=1)               # each [B, C, T]
    return torch.tanh(f) * torch.sigmoid(g)   # [B, C, T]
In the gated unit z = tanh(·) ⊙ σ(·), what role does the sigmoid branch play?

Chapter 7: The Residual Block & Full Network

We now assemble the pieces into the unit that repeats throughout WaveNet: the residual block. A network 30+ layers deep would never train without two ideas from ResNets and Highway Nets: residual connections and skip connections.

Anatomy of one block (Figure 4 of the paper)

WaveNet Residual Block — Click a node to trace it
Click any node to see what tensor flows through it.

The data flow, step by step:

  1. Input (the residual stream, [B,C,T]) enters the block.
  2. A dilated convgated activation (Ch 6) produces [B,C,T].
  3. A 1×1 conv then splits this into two paths:
    • Residual path: add the block's input back to its output. The block learns a residual (a correction), so gradients flow straight through the addition to early layers — no vanishing. Output feeds the next block.
    • Skip path: a separate 1×1 conv taps the block's activation and sends it directly to the output stage, bypassing all later blocks.
Why both residual AND skip? They solve different problems. Residual connections keep gradients alive through depth (the network can be 30+ layers). Skip connections let every layer — shallow and deep — contribute directly to the final prediction, so the output sees features at every timescale (the d=1 layer's fine detail and the d=512 layer's long context) without that information having to survive the entire stack. The paper says both are used "to speed up convergence and enable training of much deeper models."

The output stage

All the skip contributions are summed, then passed through: ReLU → 1×1 conv → ReLU → 1×1 conv → softmax. That final softmax produces the 256-way categorical distribution over the next sample. Trace the shape: skip sum [B, Cskip, T] → ... → [B, 256, T]. During training, cross-entropy is computed at every one of the T positions in parallel.

python
class ResidualBlock(nn.Module):
    def __init__(self, C, C_skip, dilation):
        self.conv = CausalDilatedConv1d(C, 2*C, kernel=2, dilation=dilation)
        self.res_1x1  = nn.Conv1d(C, C, 1)        # back to residual stream
        self.skip_1x1 = nn.Conv1d(C, C_skip, 1)   # tap to output

    def forward(self, x):
        h = self.conv(x)                          # [B, 2C, T]
        f, g = h.chunk(2, dim=1)
        z = torch.tanh(f) * torch.sigmoid(g)   # gated [B, C, T]
        skip = self.skip_1x1(z)                   # [B, C_skip, T]
        res  = self.res_1x1(z) + x              # residual add [B, C, T]
        return res, skip
What is the distinct job of the SKIP connections (vs the residual connections)?

Chapter 8: Conditioning WaveNet

So far WaveNet generates unconditional audio — it babbles in a realistic but meaningless way. To make it useful (say a particular speaker, or actual words), we give it an extra input h and model p(x|h):

p(x | h) = ∏t p(xt | x1, ..., xt−1, h)

There are two flavors, distinguished by whether the conditioning signal is constant over time or itself a time series.

Global conditioning

A single latent h that influences every timestep — e.g. a speaker-identity embedding. It's injected into the gated activation as a learned linear projection added before the nonlinearities:

z = tanh(Wf,k ∗ x + Vf,kTh)  ⊙  σ(Wg,k ∗ x + Vg,kTh)

Here V∗,kh is a vector that is broadcast over time — the same bias added at every timestep. The paper conditions on a one-hot speaker ID this way, and a single WaveNet then captures all 109 VCTK speakers, switchable by flipping the one-hot.

Local conditioning

A second time series ht, often at a lower sample rate than the audio — e.g. linguistic features (which phoneme is being spoken) for TTS, sampled every few milliseconds, not every audio sample. The challenge: align this slow signal to the 16 kHz audio. WaveNet uses a transposed convolution (learned upsampling) to stretch h up to audio resolution, giving y = f(h), then injects it via 1×1 convs:

z = tanh(Wf,k ∗ x + Vf,k ∗ y)  ⊙  σ(Wg,k ∗ x + Vg,k ∗ y)

The paper notes the alternative — just repeating the low-rate values across time instead of learned upsampling — "worked slightly worse." Learned upsampling lets the model smoothly interpolate between, say, phoneme boundaries.

What degrades without conditioning — measured: In the TTS experiments, WaveNet conditioned only on linguistic features sometimes had "unnatural prosody by stressing wrong words." The receptive field (240 ms) wasn't long enough to capture sentence-level F0 (pitch) contours. The fix was adding log-F0 as a second local-conditioning signal, predicted by an external model running at 200 Hz — which can model the long-range pitch structure WaveNet's window can't. This is a beautiful example of conditioning compensating for a receptive-field limit.
Local Conditioning — Upsample Linguistic Features to Audio Rate

The orange dots are the low-rate conditioning signal (e.g. phoneme features). The teal line is the upsampled version fed into every layer. Toggle "Learned" off to see the crude "repeat across time" alternative — note the staircase the paper found worked worse.

What is the difference between global and local conditioning?

Chapter 9: Experiments & Results

WaveNet was evaluated on four tasks. The headline: in text-to-speech, human listeners rated it more natural than the best existing systems, closing roughly half the gap to real human speech.

TTS Naturalness — Mean Opinion Score (5-point scale)

The MOS numbers (Table 1)

Mean Opinion Score: listeners rate naturalness 1 (Bad) to 5 (Excellent). Higher is better.

SystemUS EnglishMandarin
LSTM-RNN parametric3.673.79
HMM concatenative3.863.47
WaveNet (L+F)4.214.08
Natural speech (16-bit PCM)4.554.21

Read the gap-closing: in US English the best baseline was 3.86 vs natural 4.55 — a gap of 0.69. WaveNet hit 4.21, shrinking the gap to 0.34, a 51% reduction. In Mandarin the gap dropped from 0.42 to 0.13, a 69% reduction. At the time these were "the highest ever reported MOS values" on these datasets.

The four tasks

TaskSetupResult
Multi-speaker generationVCTK, 109 speakers, conditioned on speaker ID only (no text)One model captures all 109 voices; babbles realistic non-words; even mimics breathing & room acoustics
Text-to-speechLocal conditioning on linguistic features (+ log-F0)State of the art naturalness, both languages (table above)
MusicMagnaTagATune (~200h), YouTube piano (~60h)Harmonic, "aesthetically pleasing" fragments; larger receptive field = more musical
Speech recognitionTIMIT, added mean-pooling + non-causal convs, dual loss18.8 PER — best reported (at the time) directly on raw audio
The receptive-field finding, confirmed empirically: In multi-speaker generation, the model's ~300 ms receptive field meant it could only "remember the last 2–3 phonemes" — so it produced fluent-sounding gibberish, not coherent words. In music, "enlarging the receptive field was crucial to obtain samples that sounded musical." This is the entire thesis of the paper validated: receptive field is the bottleneck, and dilation is how you buy it cheaply.

Honest limitations the paper admits

What did the music and multi-speaker experiments confirm about WaveNet's design?

Chapter 10: Connections & Cheat Sheet

WaveNet sits at a pivotal junction in generative modeling: it took the autoregressive, pixel-by-pixel idea from PixelRNN/PixelCNN and proved it could conquer the most demanding sequential domain — raw audio at 16,000 samples per second.

2016
PixelRNN / PixelCNN — autoregressive image generation, pixel by pixel
2016
WaveNet — dilated causal convs bring autoregression to raw audio
2017
Parallel WaveNet — distillation into a fast, parallel generator (fixes the slow-sampling problem)
2018+
Tacotron 2 (WaveNet vocoder), WaveRNN, WaveGlow — production neural TTS
2020+
Neural audio codecs (SoundStream, EnCodec), diffusion audio, AudioLM — descendants of "model the waveform directly"

The cheat sheet — every key equation

IdeaEquation / RuleWhat each symbol means
Autoregressive factorizationp(x) = ∏ p(xt|x<t)xt = sample at time t; predicted from all earlier samples
Plain causal receptive fieldR = (K−1)·L + 1K = filter size, L = layers; linear growth
Dilated receptive field (K=2)R = 2L per blockdilations 1,2,4,...,2L−1; exponential growth
µ-law compandingf(x) = sign(x)·ln(1+μ|x|)/ln(1+μ)μ=255; warps amplitude so 256 levels match the ear
Gated activationz = tanh(Wf∗x) ⊙ σ(Wg∗x)tanh = content, σ = gate (how much passes)
Global conditioning+ VfTh inside the gateh = constant latent (speaker), broadcast over time
Local conditioning+ Vf∗y, y = upsample(h)h = time series (linguistics), learned-upsampled to audio rate

Why WaveNet still matters

The one-paragraph summary you could give on a whiteboard

WaveNet models audio autoregressively: each of 16,000 samples per second is a 256-way softmax conditioned on all previous samples. The conditional is computed by a stack of dilated causal convolutions — causal so it never sees the future, dilated (1,2,4,...,512) so the receptive field grows exponentially and reaches thousands of samples with ~30 layers instead of thousands. Inputs are µ-law companded to 256 categories so the softmax is tractable and the quantization matches human hearing. Each layer uses a tanh⊙sigmoid gated activation; residual connections keep the deep stack trainable and skip connections feed every timescale to the output. Conditioning (speaker ID globally, linguistics locally) turns the babbler into a controllable TTS system that beat every prior method on naturalness.

"...generating wideband raw audio waveforms, signals with very high temporal resolution, at least 16,000 samples per second."
— van den Oord et al., WaveNet (2016)