A Deep Q-Network is a value-based reinforcement learning method that trains a neural network to predict the expected cumulative reward — the Q-value — for each action available in a given state. Rather than learning a policy directly, the agent learns to evaluate actions and always picks the one with the highest predicted value. In CartPole, this means the network receives the four state observations (cart position, cart velocity, pole angle, pole angular velocity) and outputs two numbers: the estimated long-term reward for pushing left and for pushing right. The agent then selects whichever action scores higher. Over thousands of training episodes, and with the help of experience replay and exploration decay, those Q-value estimates converge toward a policy that keeps the pole balanced.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/cartpole/llms.txt
Use this file to discover all available pages before exploring further.
The JavaScript implementation uses manual gradient descent with no automatic differentiation library. This is a deliberate simplification to keep the entire demo self-contained in a static browser page — no Python runtime, no TensorFlow.js, no external dependencies. The network architecture and hyperparameters are identical to those in the original Keras notebook.
Network Architecture
The DQN uses a three-layer fully-connected network. The input is the four-element state vector; the output is a pair of Q-values, one per action.| Layer | Units | Activation |
|---|---|---|
| Input | 4 (state size) | — |
| Hidden 1 | 24 | ReLU |
| Hidden 2 | 24 | ReLU |
| Output | 2 (action space) | Linear |
Cartpole.ipynb
predict() method in dqn-agent.js applies the same three-layer dense forward pass, using a dense() helper that optionally applies ReLU:
dqn-agent.js
[−0.08, 0.08] (i.e. (Math.random() - 0.5) * 0.16). Biases are initialised to zero.
Q-Value Update (Bellman Equation)
The central learning equation is the Bellman update. After the agent takes an action and observes the result, the target Q-value for that action is revised to incorporate the actual reward plus a discounted estimate of the best future outcome:Cartpole.ipynb
trainSample():
dqn-agent.js
GAMMA = 0.95 means the agent places strong weight on future rewards — a reward ten steps from now is worth about 0.95^10 ≈ 0.60 of its face value today. This matters for CartPole because the consequences of any single push are delayed: a small over-correction creates a lean that takes several steps to become a fall. An agent with a low gamma would be too short-sighted to learn stable balancing.
The clipped error Math.max(-10, Math.min(10, target - output[sample.action])) bounds the gradient update to the range [−10, 10], preventing large Q-value errors from destabilising the weights during early training when predictions are poor.
Exploration vs. Exploitation
The agent uses an epsilon-greedy strategy to balance exploring new actions with exploiting what it has already learned.| Parameter | Value | Meaning |
|---|---|---|
explorationRate (ε) | 1.0 initial | Fully random at the start |
explorationDecay | 0.995 | Multiplied against ε after every experienceReplay() call |
explorationMin | 0.01 | ε never drops below 1% — the agent always retains some randomness |
act() method in dqn-agent.js:
dqn-agent.js
explorationRate is high, the agent ignores the network entirely and picks left or right at random. As it decays, the agent increasingly acts on its learned Q-values. The random baseline (50/50 left/right) means that even fully random early episodes still generate useful training data — the cart moves and the pole falls in varied ways, producing a diverse set of experiences to learn from.
Experience Replay
Rather than learning from transitions immediately as they happen, the agent stores every(state, action, reward, nextState, terminal) tuple in a replay memory and trains on random batches drawn from that buffer.
| Parameter | Value | Meaning |
|---|---|---|
memorySize | 1,000,000 | Maximum number of stored transitions |
batchSize | 20 | Transitions sampled per training step |
remember() method appends a transition and trims the buffer if it exceeds capacity:
dqn-agent.js
experienceReplay() method samples batchSize random transitions, trains on each one, then decays epsilon:
dqn-agent.js
Backpropagation
ThetrainSample() method implements a partial manual backpropagation pass. There is no automatic differentiation — the gradient is computed explicitly.
The output layer receives a direct weight update proportional to the error on the chosen action:
dqn-agent.js
0.2 to limit the magnitude of the indirect update:
dqn-agent.js
w1) are not updated. This partial backprop is a deliberate simplification: the output layer, which maps learned features to Q-values, does most of the learning work; partially propagating to the second hidden layer provides enough feature adaptation for the agent to solve CartPole without requiring a full multi-layer gradient computation in plain JavaScript.