Pong in Hardware
Two paddles, a bouncing ball, and a 14-phase rendering pipeline — all built from logic gates, registers, and memory. No CPU runs this game. Every decision is wired directly into the circuit.
This is a complete Pong game running on a 16×16 screen — no CPU, no software, just digital circuits. Click Run and use W/S for the left paddle and ↑/↓ for the right. Scroll down to see how each piece works.
There is no software executing instructions here. The keyboard scan codes flow through comparators, the mux trees select deltas, the adders compute new positions, and the phase counter orchestrates memory writes — all in parallel combinational logic, driven forward by the clock.
This is the same principle behind dedicated hardware accelerators: instead of a general-purpose CPU interpreting instructions one by one, the “program” is the circuit topology itself. It can only play Pong, but it does so at one operation per clock cycle per gate.
A Moving Ball
The ball’s position is stored in two Register components — one for X and one for Y. Each clock tick, an Adder adds a velocity delta (dx, dy) to the current position. A BitSlice wraps the result to the 0–15 range so the ball stays on our 16×16 screen.
Toggle the enable switch, then click Tick to watch the ball move. Try changing dx and dy to alter the ball’s direction.
Bounce Detection
When the ball reaches the top or bottom edge of the screen, it needs to reverse direction. Two Comparator nodes check if the ball’s Y coordinate equals 0 (top wall) or 15 (bottom wall). An Or gate combines the results, and a Mux flips the velocity: moving down (+1) when bouncing off the top, moving up (−1) when bouncing off the bottom.
Change ballY to 0 or 15 and watch the bounce LED light up.
Paddle Movement
Each paddle is controlled by a pair of keys. The circuit compares the keyboard scan code to the expected values: W (17) moves up, S (31) moves down. A cascaded Mux tree converts the key detection into a delta value: −1, 0, or +1.
The delta feeds into an Adder that updates a Register storing the paddle’s Y position. A BitSlice wraps the result to keep the paddle within the 16-pixel screen height. Set keyboard to 17 (W) or 31 (S), toggle the enable switch, and tick to see it move.
The 14-Phase Rendering Pipeline
Pong has three objects to draw (ball, left paddle, right paddle), and the DualPortRAM only supports one write per clock cycle. The solution: a 14-phase pipeline that takes turns.
- Phase 0: clear the old ball
- Phases 1–3: clear the old left paddle (3 pixels tall)
- Phases 4–6: clear the old right paddle
- Phase 7: draw the new ball
- Phases 8–10: draw the new left paddle
- Phases 11–13: draw the new right paddle
A register counts from 0 to 13, wrapping back to 0 when it reaches 14. The Draw LED lights when the counter is in a draw phase (≥ 7). Toggle the enable switch and tick to watch the cycle.
From Coordinates to Pixels
The screen’s DualPortRAM stores 256 pixels in a flat array. To draw at position (X, Y) we need the linear address Y × 16 + X. Multiplying by 16 is the same as shifting left by 4 bits, and in real hardware a left shift costs zero gates — it’s just wiring. Each bit of Y connects to a position four places higher in the output, with the low four bits tied to zero. The only actual logic gate is the final adder that adds X.
Change x and y to see the address update instantly — this is pure combinational logic with zero clock cycles.