Move toward the star. Catch it before it lands.Training: 0 / 0 catches
Turn the knobs
Changes apply live
Inside the brain
4 inputs → 8 neurons → 3 actions
Lines show current weights: blue positive, purple negative; thicker means stronger. Hidden-node fill shows activation magnitude. Inputs and scores describe the last decision, before its learning update.
Left+0.00Action value
Stay+0.00Action value
Right+0.00Action value
Action values estimate future reward, not probabilities. Higher is better.
Is it learning?
Training only
—Catch rate · last 100
0Rounds completed
—Smoothed training loss
The horizontal axis always starts at round 0. Each point measures up to 100 recent rounds; older checkpoints are thinned to retain the full run. The first measurement appears at round 10. Random exploration can cause misses even after learning. Lower loss alone does not prove better play.
A lesson in four moves
01 / OBSERVE
Where is everything?
The network sees paddle position, star position, time left, and the gap between them. It gets numbers, not pixels.
02 / CHOOSE
Try a move.
Eight neurons turn the inputs into three action values. Pick the highest, or explore with a random move.
03 / GET FEEDBACK
Did that help?
A catch earns +1; a miss earns −1. Each earlier move earns twice the reduction in distance to the star. This extra coaching makes the game easier to learn.
04 / UPDATE
Adjust the connections.
The network revisits past moves and adjusts its weights through backpropagation, making its reward predictions a little less wrong.
Try this
Train for 300 rounds at 50×. Pause, switch on “Watch learned policy,” then resume. Reset and compare 0% exploration with 100%. A fully random player can still learn from experience—but you will only see its learned choices in watch mode.
What exactly runs here?
A real, small Deep Q-Network trained in your browser: 4 inputs, 8 tanh neurons, 3 linear outputs, and 67 trainable weights and biases. It replays 16 experiences per game step from a buffer of up to 2,000. A separate target network is copied every 120 training steps. The discount factor is 0.9; updates use Huber loss and stochastic gradient descent with a per-sample step size of learning rate ÷ 4. Replay starts after 16 moves.
The star falls straight down over 12 moves. The paddle moves 8% of the field per action and catches within 9.5% of the star’s horizontal position. Each round starts at random positions. This is a small, coached environment; it is not a demonstration of learning complex games from images.
Pause preserves the exact game state. Stop starts a fresh round and keeps the network and training history. Reset clears all learning and scores using the same starting seed, while keeping your knob settings. Changing modes starts a fresh round; entering watch mode clears watch scores. Reloading the page clears everything. At high speeds, the display samples the simulation rather than showing every move.
For the underlying method, see the PyTorch DQN tutorial ↗. This experiment implements the math in TypeScript and has no model download or server training.
UNDER THE HOOD
From a falling star to a weight update
Follow one decision through the architecture, the equations, and the actual TypeScript that runs above. Open each step when you want to go deeper.
01 The architecture: 4 → 8 → 3
This is a fully connected feedforward network: every input connects to every hidden neuron, and every hidden neuron connects to every output. One pass produces a score for each possible move.
INPUT4 numbers
Position, time, gap
HIDDEN LAYER8 tanh neurons
Weighted sums + nonlinearity
OUTPUT3 action values
Left · stay · right
The 67 trainable parameters
Part
Shape
Count
In the code
Input → hidden weights
8 × 4
32
w1
Hidden biases
8
8
b1
Hidden → output weights
3 × 8
24
w2
Output biases
3
3
b2
(8×4)+8+(3×8)+3=67
A weight controls the strength and sign of a connection. A bias shifts a neuron’s weighted sum, even when its inputs are zero. The hidden layer’s tanh activation bends the function so the network can learn nonlinear relationships.
Weights start randomly: first-layer weights between −0.5 and +0.5, second-layer weights between −0.35 and +0.35. Biases start at zero. The hidden layer is deliberately fixed at eight neurons so changing training settings compares the same architecture.
02 Inputs and the forward pass
Let p be the paddle’s horizontal position, s the star’s position, and t the number of moves already taken. Positions use field coordinates from 0 to 1. The state vector is:
xt=2pt−12s−11−t/12s−pt∈R4
The first two inputs center positions around zero. The third is the fraction of moves remaining. The signed gap says which side the star is on. That gap is redundant information, but handing it to this tiny network helps it learn.
Here h holds the eight hidden activations and q holds the three action values. Tanh keeps each hidden activation between −1 and +1. The output layer is linear: reward estimates may be negative or greater than one, so there is no softmax or conversion into probabilities.
Forward pass · Network.forward
forward(input: number[]) { const hidden = this.w1.map((row, j) => Math.tanh(row.reduce((sum, w, i) => sum + w * input[i], this.b1[j]))); const values = this.w2.map((row, k) => row.reduce((sum, w, j) => sum + w * hidden[j], this.b2[k])); return { hidden, values };}
03 Choosing a move and earning a reward
The exploration slider sets ε (epsilon). With probability ε, choose uniformly from all three actions. Otherwise, choose the highest action value. This is called an epsilon-greedy policy (Sutton & Barto, 2018, §2.2).
at={uniform random action,argmaxaQθ(xt,a),with probability εwith probability 1−ε
Qθ means the value predicted by the network with its current weights and biases, collectively called θ (theta). Actions 0, 1, and 2 mean left, stay, and right. A random choice can still pick the best action; tied best scores choose the first action in this implementation.
Exploration versus the network’s favorite · StarTrainer.step
The paddle moves 0.08 field widths per action and is clamped to [0.08, 0.92]. Write dt = |s − pt| for its distance from the star before moving:
rt=⎩⎨⎧+1,−1,2(dt−dt+1),t=11 and dt+1≤0.095t=11 and dt+1>0.095t<11
The final move replaces the distance reward with catch or miss feedback. Earlier moves get positive feedback for closing the gap and negative feedback for widening it. This reward shaping supplies useful coaching before the end of the round.
Movement and reward · StarTrainer.step
const before = Math.abs(this.star - this.paddle);this.paddle = Math.max(0.08, Math.min(0.92, this.paddle + (this.action - 1) * 0.08));this.tick++;const after = Math.abs(this.star - this.paddle);this.terminal = this.tick === 12;const caught = after <= 0.095;// Dense feedback makes early learning visible; terminal success is still// rewarded separately. This is deliberately a shaped, easy environment.this.reward = this.terminal ? (caught ? 1 : -1) : 2 * (before - after);
04 Learning from replay and future rewards
Each training move stores an experience: current inputs, chosen action, reward, next inputs, and whether the round ended. The replay buffer holds the latest 2,000 experiences. Once it has 16, each new move triggers 16 random samples with replacement, so an experience can be selected more than once.
(xt,at,rt,xt+1,donet)
For each sampled experience, form a target y: the reward already received, plus a discounted estimate of what could come next. The target network’s parameters are written θ−. At the end of a round there is no future reward to add.
y=rt+γ(1−donet)a′maxQθ−(xt+1,a′),γ=0.9
The done flag is 1 at the end of a round and 0 otherwise. The discount factor γ (gamma) makes more distant rewards count less. Sampling old moves reduces dependence on the latest trajectory; the delayed target copy helps avoid chasing a target that changes after every weight update. The deep Q-network (DQN) combined both with Q-learning; the history below says where they came from (Sutton & Barto, 2018, §16.5).
Replay, target values, and target-network refresh · StarTrainer.step
if (this.memory.length >= 16) { let loss = 0; for (let i = 0; i < 16; i++) { const sample = this.memory[Math.floor(this.random() * this.memory.length)]; const target = sample.reward + (sample.done ? 0 : 0.9 * Math.max(...this.target.forward(sample.next).values)); loss += this.network.train(sample.state, sample.action, target, settings.learningRate / 4); } this.loss = 0.95 * this.loss + 0.05 * loss / 16;}if (this.steps % 120 === 0) this.target.copyFrom(this.network);
These targets are estimates, not labels supplied by an expert player. This is why reinforcement learning can be noisy: the network learns partly from its own evolving predictions. The PyTorch DQN tutorial explains replay and a target network in a larger example; it updates its target network gradually rather than copying it at intervals (PyTorch DQN tutorial, n.d.).
05 Loss, backpropagation, and the weight update
Compare the chosen action’s prediction with its target. Write this error as e. Huber loss is quadratic for small errors and linear for large errors, which limits how strongly one large prediction error drives an update. Peter Huber introduced this function in 1964 for estimates that stay reliable when some observations contain gross errors (Huber, 1964).
The factor 1 − hj2 is the derivative of tanh. Hidden gradients must use the output weights before they are updated. Only the selected output row receives a direct gradient; shared hidden-layer changes can still affect every action value.
θ←θ−η∇θL,η=4learning-rate slider
Each of the 16 replay samples updates the weights immediately. This is 16 sequential SGD updates, rather than a single update from an averaged batch gradient. The displayed slider value is divided by four for each update; at the default 0.01, the per-sample step size is 0.0025. The target is treated as fixed during differentiation.
The loss display is a smoothed mean of the 16 sampled losses from each training move. Because the samples update the network sequentially, these losses are measured at slightly different weights.
Lk=0.95Lk−1+0.05(161m=1∑16Lk,m)
Here k counts replay-training passes; the running value starts at zero. Early readings are therefore pulled toward zero. A small loss means sampled reward predictions fit their current targets. It does not guarantee a high catch rate.
Learning rate: compare how quickly the catch rate improves and how much it wobbles. A higher setting makes larger parameter updates.
Exploration: random actions create varied experience but can reduce the training catch rate. Exploration stays fixed at your chosen value; there is no automatic decay.
Speed: changes how many game moves run per second. Compare runs after the same number of completed training rounds, not after the same wall-clock time.
Watch mode: disables both exploration and weight updates. Its separate catch count is a clearer check of the learned policy.
Reset gives you the same initial seed and clears the network, buffer, and scores while retaining your slider settings. Set the controls before starting each comparison. Treat any single result cautiously: changing exploration also changes the random sequence of later experiences.
</> Read the complete training engine
This is the source used by the simulation, included directly at build time.
Q-learning comes from Christopher Watkins’s 1989 PhD thesis. Watkins and Dayan later proved that it converges to the optimal action values with probability 1, provided every action keeps being tried in every state and the values are stored discretely, one entry per state and action (Watkins & Dayan, 1992). They also point out that Watkins had shown it may not converge correctly with other representations. A neural network is one of those: this lesson has no convergence guarantee. Function approximation, bootstrapped targets and training on replayed off-policy data can together make learning unstable, and this lesson combines all three (Sutton & Barto, 2018, §11.3).
Experience replay was first studied by Long-Ji Lin in 1992 (Sutton & Barto, 2018, §16.5). Mnih and colleagues combined it with Q-learning and a convolutional network that learned directly from Atari screen pixels (Mnih et al., 2013). The 2015 deep Q-network (DQN) learned 49 Atari games with one architecture and set of settings (Mnih et al., 2015). It also added a target network, copied from the learning network after a fixed number of updates, and clipped the error term to [−1, 1] (Sutton & Barto, 2018, §16.5). That clipping gives the same gradient as the Huber loss with threshold 1 used here.
This lesson keeps those three pieces at toy scale. DQN used minibatches of 32 transitions, the RMSProp optimizer and exploration that decreased over the first million frames (Sutton & Barto, 2018, §16.5). Here there are 67 parameters, four hand-designed inputs, distance-based coaching rewards, fixed exploration and one plain gradient step per sampled experience. The pixel lesson follows the history from hand-designed inputs to pixels.
References and further reading
References
Peter J. Huber (1964). Robust Estimation of a Location Parameter. The Annals of Mathematical Statistics 35(1), 73–101.Abstract available · Read: Project Euclid abstract, which states the most robust M-estimator’s ρ function · Checked 2026-09-13
Volodymyr Mnih, Koray Kavukcuoglu, David Silver, Alex Graves, Ioannis Antonoglou, Daan Wierstra & Martin Riedmiller (2013). Playing Atari with Deep Reinforcement Learning. arXiv:1312.5602 [cs.LG].Open access · Read: Abstract, §1 introduction and §4 deep reinforcement learning (Algorithm 1) · Checked 2026-09-13
Volodymyr Mnih, Koray Kavukcuoglu, David Silver, Andrei A. Rusu, Joel Veness, Marc G. Bellemare, Alex Graves, Martin Riedmiller, Andreas K. Fidjeland, Georg Ostrovski, Stig Petersen, Charles Beattie, Amir Sadik, Ioannis Antonoglou, Helen King, Dharshan Kumaran, Daan Wierstra, Shane Legg & Demis Hassabis (2015). Human-level control through deep reinforcement learning. Nature 518(7540), 529–533.Publisher record; full text may require access · Read: Publisher page: summary, abstract and opening paragraphs; method details as described in Sutton & Barto (2018) §16.5 · Checked 2026-09-13
Adam Paszke & Mark Towers (n.d.). Reinforcement Learning (DQN) Tutorial. PyTorch Tutorials.Open access · Read: Existing lesson link; tutorial text on replay memory, Huber loss, epsilon-greedy selection and its soft target-network update · Checked 2026-09-13
Richard S. Sutton & Andrew G. Barto (2018). Reinforcement Learning: An Introduction. MIT Press, 2nd ed.Library record · Read: Authors’ free PDF of the second edition (2020 printing, incompleteideas.net): §2.2 action-value methods, §6.5 Q-learning, §11.3 the deadly triad, §16.1 TD-Gammon, §16.2 Samuel’s checkers player, §16.5 human-level video game play · Checked 2026-09-13
Christopher J. C. H. Watkins & Peter Dayan (1992). Q-learning. Machine Learning 8(3–4), 279–292.Publisher record; full text may require access · open copy · Read: Author copy: abstract, introduction and statement of the convergence conditions · Checked 2026-09-13
Further reading
Ian Goodfellow, Yoshua Bengio & Aaron Courville (2016). Deep Learning. MIT Press, 1st ed.Chapter 6 covers feedforward networks and backpropagation; chapter 8 covers stochastic gradient descent and why training can be difficult.Open access · Checked 2026-09-13