
Build a Centipede-Style Fixed Shooter in Phaser 3
Build a Centipede-Style Fixed Shooter in Phaser 3
Learn how an arcade fixed shooter turns grid obstacles, chain splits, and poison mushrooms into tense play, then build the Phaser 3 version in one HTML file.
Centipede turned a simple screen into a pressure cooker: a segmented creature descends through a destructible field while the player chooses when to shoot, dodge, and accept risk. This post ships with a playable browser prototype, built in one HTML file, that applies those ideas in Phaser 3.
Transparency note: This post is desk research based on official Phaser documentation, the Wikipedia article on Centipede, and the shipped prototype embedded in this page. No hands-on playtesting of Atari’s 1981 arcade cabinet took place.
Controls: arrow keys or WASD to move the Bug Blaster, Space to fire (hold to auto-fire). On a phone, drag anywhere to move: the ship fires automatically.
Why Centipede is a beginner fixed shooter that teaches clean state machines
Centipede is a beginner-friendly fixed shooter because its rules combine movement, shooting, destructible cover, and enemy behavior without hiding those systems inside a physics engine. Its history also shows why careful state design matters: the game paired simple controls with patterns that created readable decisions under pressure.
Released in North America in August 1981, Centipede was developed and published by Atari, Inc. The Wikipedia article on Centipede credits Dona Bailey and Ed Logg. Logg, also the designer of Asteroids, did the design, while Bailey wrote about half of the programming and was one of the few women programming games in the industry at the time.
It was one of the first coin-operated arcade games with a significant female player base. How to Win Video Games estimated in 1982 that about half of Centipede’s players were women, against 95% men for Defender. The game ranked as the third-highest-grossing arcade game in the United States in 1982, tied with Donkey Kong and behind Ms. Pac-Man and Pac-Man. The Atari VCS, or 2600, port sold 1,475,240 cartridges during 1982-1983 and made it the 11th-best-selling Atari game.
That history matters because simple controls can support complex decisions. State machines keep player, projectile, mushroom, chain, and enemy updates separate, so one collision cannot advance another system. Inducted into The Strong World Video Game Hall of Fame in 2020, the game also offers a direct lesson: make transitions visible and deterministic.
What the playable prototype in this post includes
The shipped prototype in this post is a complete, single-file study in fixed-shooter structure. It uses Phaser 3.90.0 from jsDelivr, needs no build step or image assets, and draws every sprite procedurally, while its portrait canvas, grid, HUD, enemies, scoring, lives, and saved high score form one coherent game loop.
The file runs on a 480 by 640 canvas in a 3:4 frame. A 15 by 18 grid uses 32-pixel cells, and a monospace HUD occupies the top band. Phaser.Scale.FIT with autoCenter: Phaser.Scale.CENTER_BOTH fits and centers the canvas. The Phaser 3 Getting Started guide covers framework setup; the game rules remain plain browser JavaScript.
One Phaser Graphics object is cleared and redrawn every frame, so there are no image assets. The visual layer can stay simple: it reads state, then draws it. The game remains animated because the redraw receives interpolated positions between logical grid steps.
The centipede is an array of cells rather than a single sprite. Its head chooses a step, each segment takes the cell just vacated by the one ahead, and the chain quickens as it descends. A blocked edge or mushroom causes a row drop and reversal. A middle-segment hit leaves a mushroom and turns the rear cells into a separate chain with a faster interval. Poisoned mushrooms instead trigger a straight dive.
Three additional enemies shape the field. The Spider descends in zigzags and eats mushrooms, the Flea drops a trail while the field is sparse, and the Scorpion crosses the upper area and poisons what it touches. The player has three lives, an extra-life award, and a localStorage high score. A fixed-step accumulator drives movement, while hand-written AABB checks, including a swept vertical test for bullets, handle collisions without Phaser Arcade Physics.
Code walkthrough: chain following, splitting, mushrooms, and wave escalation
The code walkthrough follows the prototype’s data flow rather than its visual flourishes. A fixed-step accumulator advances grid cells, an array preserves body order, small update functions own enemy transitions, and one redraw pass presents the resulting state. This separation keeps movement deterministic even though rendering interpolates smoothly between cells.
Phaser 3.90.0 arrives from the CDN, while Phaser 3 Getting Started supplies the framework context. The game loop stays under your control: separate a logical update from frame redraw, make one chain step produce one new cell, and let only presentation code interpolate. These short excerpts condense the prototype’s core update patterns; helper functions handle collision and drawing details.
function stepChain(chain, dx, dy) {
const head = chain.cells[0];
const next = { x: head.x + dx, y: head.y + dy };
if (isBlocked(next)) return dropAndReverse(chain);
chain.previous = chain.cells.map(cell => ({ ...cell }));
chain.cells.unshift(next);
chain.cells.pop();
}
The head is index zero. unshift puts its destination first and pop removes the old tail, so every old head position becomes the next segment’s destination. Saved cells give the renderer interpolation endpoints. A blocked cell calls the separate drop-and-reverse path instead of leaving the grid.
A split is an array lifecycle operation, not a special animation-only effect. Save the cells behind the hit before changing the active chain, omit the destroyed segment, and create a new enemy from the saved array.
function splitAt(chain, index, cell) {
const rear = chain.cells.slice(index + 1);
chain.cells = chain.cells.slice(0, index);
mushrooms.set(cell.key, { ...cell, hits: 4 });
spawnCentipede(rear, { fasterStep: true });
}
The slices create independent arrays. The first rear cell becomes the new head, and fasterStep selects a shorter interval. The mushroom in the vacated cell immediately changes future navigation, so a shot has consequences beyond removing one target.
Store each mushroom by a stable grid key and keep its remaining hits in that record. Collision code retrieves the record, updates it, and removes it only when the count reaches zero.
function hitMushroom(cell) {
const key = cell.key;
const mushroom = mushrooms.get(key);
if (!mushroom) return;
mushroom.hits -= 1;
registerMushroomHit();
if (mushroom.hits === 0) mushrooms.delete(key);
}
Rebuilding the map creates fresh records for the next wave. Since one graphics object visits every active record during redraw, no per-mushroom texture lifetime is needed.
Wave difficulty is best kept as data and policy, not scattered through collision handlers. The prototype starts with a shorter chain as waves rise, moves from a 190 ms interval toward a 72 ms floor, and produces a denser mushroom field.
const wavePolicy = {
firstSegments: 12,
secondSegments: 11,
segmentFloor: 4,
firstStepMs: 190,
stepFloorMs: 72
};
function startWave(wave) {
const rules = resolveWaveRules(wave);
spawnCentipede(rules.segments, rules.stepMs);
generateMushroomField(rules.density);
}
The second wave uses 11 segments, later waves stop at 4, and density rises with the wave. resolveWaveRules keeps progression in one place. Build a fresh field and active chain at the wave boundary, then resume the normal loop.
The risk/reward score design that made Centipede a hit
Risk and reward work best when the scoring table mirrors where danger appears on the field. In Centipede, mushrooms alter navigation, the chain threatens the lower lanes, and special enemies enter different zones, so point values are also instructions about where the player should move and when hesitation becomes expensive.
The original arcade scale is 1 / 10 / 100 / 200 / 300/600/900 / 1,000: in order, the values cover a mushroom hit, body segment, head, flea, Spider’s three distance bands, and the Scorpion, the highest target. A mushroom takes four hits and the flea takes two, rewarding repeated exposure. The prototype retains that pressure with an extra life at 12,000 points, turning survival into a concrete goal.
The Spider makes distance a gamble. It zigzags through the player’s band and eats mushrooms, but its distance bands pay more when met higher than near the bottom. The Scorpion crosses the upper field and poisons mushrooms, so pursuing its top payout pulls the player from the safer lower route and changes the terrain controlling the chain.
Poison adds another decision. A side-stepping chain can become a direct vertical threat, so clearing or preserving a mushroom makes the next wave easier or harder. Head and body values reward identifying a chain’s front, while special enemies reward leaving a comfortable firing position. These awards affect movement, cover, or progress toward a bonus life.
Controls, mobile feel, and persistence
The prototype preserves the original arcade control idea while adapting input for a browser. Movement stays on the grid, keyboard and touch produce the same positional goal, and the portrait frame scales cleanly. Auto-fire on touch removes the need to invent a second mobile button, while localStorage gives the short session a durable score target.
Arrow keys or WASD change the Bug Blaster’s grid target, and holding Space repeats fire input. On a phone, dragging anywhere in the canvas moves the ship while firing continues automatically, avoiding a crowded on-screen control. Input changes intent, but movement remains stepped and snapped, so quick drags cannot push the ship between cells or make collisions ambiguous.
The portrait scale settings let the 480 by 640 game occupy the available iframe width while preserving its 3:4 shape. The monospace HUD stays in the top band, and interpolation softens the motion between adjacent cells. The high score is read from and written to localStorage, while three lives and the extra-life award frame each run. Together, these choices keep the browser version approachable without removing the original arcade’s positional tension.
FAQ
The frequently asked questions here concern the prototype’s three most important implementation choices: deterministic chain motion, explicit array surgery after a shot, and browser-friendly mobile input. Each answer connects a visible arcade behavior to a small rule that can be inspected directly in the single-file game.
What keeps the centipede chain following without physics jitter?
Grid cells, not pixel positions, are the authoritative state. A fixed-step accumulator advances the head one cell at a time, every segment copies the cell just vacated by the one ahead, and rendering interpolates between the old and new cells. Because collision checks use the same deterministic cells, the chain stays aligned without Arcade Physics jitter.
How does shooting a middle segment split the chain?
When a bullet hits a middle segment, the code saves the cells behind the hit as a new array, removes the struck cell from the original chain, and creates a mushroom at that location. The front chain keeps its identity, while the rear array becomes a separate centipede with a new head and a slightly faster step interval.
Can I play the browser prototype on a phone?
Yes. Drag anywhere on the canvas to move, and the ship fires automatically. The game uses a 480 by 640 portrait canvas with Phaser scaling, so the embedded frame fits its width, centers the canvas, and leaves a clean letterbox area when proportions differ.
What you learned
This prototype shows how explicit state machines, clear array lifecycle rules, deterministic grid movement, and data-driven difficulty can produce readable tension from simple rules. Mushrooms, chain splits, poison dives, special enemies, and risk-and-reward scoring connect those systems. The next step is to play the embedded game, notice which decisions create risk, and adapt its patterns to your Phaser 3 project.