Skip to content
Work in progress. These docs describe Spawnite at launch, and some parts are still being built.

Holdfast

Holdfast is a co-op wave survival for up to four players, built on the engine and played in a room. Its source is public, under MIT-0, so you can start your own copy and change it into your game.

The following commands write a copy of Holdfast, install it and play it in a local room:

Terminal window
npx @spawnite/cli create my-holdfast --template holdfast
cd my-holdfast
pnpm install
spawnite play start

spawnite play start starts a room beside the dev server and opens the game in it; --pages 2 opens a second warden’s page, and --bots 3 adds three bots that play until the session stops. A development build mounts the devtools, so the same session steps and shoots the game. The same source is in the examples repository.

What the engine supplies and what the game wrote

Section titled “What the engine supplies and what the game wrote”

138 of Holdfast’s 169 source files import the engine, about 35,000 lines in all (measured 2026-10-05). The following table splits a survival game the way Holdfast is built:

Part The engine supplies Holdfast wrote
Players and room The hosted room, each player’s seat and reconnect, and each warden’s movement, predicted on its client Gathering at the fire, readiness, and joining a run already going
Combat Guns as weapons the room judges, with rewind and hit zones; health, damage and being downed Each gun’s numbers, the three elements and their reactions, weak spots, getting up
Monsters Chasing the nearest warden, and drawing every animated monster in a skinned batch Each wave, where it spawns, named waves, the colossus, the spitter and elites
The run State machines that hold each phase The night’s phases, from dusk to dawn and into Endless
Progress Saves and the wallet What the save keeps, the coins each kill pays, the cards and the fire
Look and sound The day clock and sky, the camera and its shake, sounds and footsteps, the HUD’s panels, counters and bars The dusk look, the models, the sounds, and where the HUD puts each number
Testing Bots that join a room, and the headless runs that step it What a bot does in a run

Two pages continue this one: guns, cards and career, and the room, the look and the HUD.

Each warden is one player’s character, and the wardens hold a stone circle through one night against waves of monsters, the Hollow. The night is fifteen waves from dusk to dawn: each wave is larger and a little faster than the last, a colossus leads the fifth, the tenth and the fifteenth, about one wave in three has a name that changes what it sends, and the sky moves toward sunrise as the waves go by. Each warden fights with an element, Storm, Ember or Frost, picked as the wardens gather, and two elements that meet on one monster set off a reaction. Every warden earns every coin the Hollow drop, and spends their own at the fire: a reroll of the cards, a gun from its rack, an upgrade of the gun in hand, or a bigger fire. Between waves the wardens rest by the fire and each takes one of three upgrade cards. Holding the fifteenth wave wins the night, and the wardens may go on into Endless, where the waves keep climbing. The run ends when every warden whose player is connected is down, unless a warden alone is getting herself up, on a screen with the wave reached and the run’s awards, and the wardens gather at the fire for the next. The Holdfast design page describes the game it is building toward.

A run moves through the following phases, which the room keeps on one siege entity and streams to every page:

  1. Waiting. The wardens gather at the fire and get ready, as Gathering says. The run starts 5 seconds after every warden in the room is ready.
  2. The first breather. Eight seconds to find the others and the fire. It asks nothing, since every warden readied for the run a moment before: no ready, no ring, and it always runs its eight seconds. The banner names the night’s goal.
  3. A wave. The Hollow rise from rifts 16 to 22 m from a random standing warden and no more than 26 m from the fire, in batches, each batch together along the ring from one rift. In waves 1 and 2 a batch rises within 60 degrees of where that warden’s camera faces, where the arena leaves room in front of her, and anywhere on her ring otherwise, so a new player sees the first monsters come. The wave is held once all of it has spawned and fallen. A monster pushed off the field, more than 8 m past the arena’s 26 m and more than 22 m from every warden, rises again with the wave’s next batch, as does one that has stood 10 s without moving a metre, farther from every warden than its reach and 8 m, where a slope too steep to climb or the scatter holds it. So no wave waits on a monster nobody can reach.
  4. A breather. Up to 20 seconds, after a beat of 1.5 seconds in which the held wave lands: every downed warden gets up with half her health, the last monster’s coins fly in, and the call names the wave held. Then each warden is dealt three cards and the breather counts down. A warden inside the fire’s ring, 7 m and a metre more for each level the wardens fed it to, heals 15 health a second through the beat and the breather. The fire roars up, a low curtain of firelight marks where its ring reaches, and embers rise off each warden it heals. The banner names the wave to come when it has a name or a colossus. Once every warden whose player is connected is ready, the breather ends 3 seconds on: a warden is ready once she steps into the ring by the fire, lit green through the breather as while the wardens gather, presses R, or taps Ready on the banner, and R or the button takes it back. Every wave that opens takes each warden’s ready away again, and a warden already standing in the ring as a breather opens is not ready until she steps in again.
  5. Dawn. Holding the fifteenth wave wins the night. Dawn breaks over the circle, every downed warden gets up, and the dawn screen shows the waves held, the Hollow the wardens felled, each warden’s coins, kills and cards, and the awards. No card is dealt. Into Endless readies her as R does, and Back to the circle closes the screen; the wardens gather at the fire as they do for a run, and once every warden is ready the run counts down 5 seconds and goes on into Endless.
  6. Endless. The run keeps its wave, cards, coins, guns, fire and kills, and a breather opens before wave 16. Waves keep climbing as the night’s do, a colossus leads every fifth, two waves of every five have a name, and there is no second dawn.
  7. The end. Once no warden whose player is connected is standing, and no warden alone is getting herself up, the run is over. The run holds 2 seconds on that last fall first, as Left 4 Dead holds on its fallen team: nothing spawns, no wave is held and no breather counts, every page draws each warden down where she fell, and a warden’s own screen says she is down and why the run ends. Then the end screen says why the circle fell, a warden alone’s second fall with her self-revive spent or every warden down at once, as the room decided it at the fall, so a teammate who drops while it shows changes nothing, and shows the wave reached, the best wave in the room, each warden’s coins, kills and cards, and the awards. Nothing moves behind it, and every warden gets up at her place by the fire. The screen frees the cursor while it shows, and the click that closes it takes the lock back. Go again readies her as R does, and Back to the circle closes the screen; either way the next run starts at dusk from the ring as the first did, every warden holding the blaster again and the fire back to level 0, unfed.

The wave reached is the shared score, and dawn the win. Each warden spends their own coins, and every warden earns the same.

A lit ring of 3.2 m round the fire marks where the wardens get ready, as the pads of Tower Defense Simulator and the elevators of Doors do. Its outer edge is the edge the room readies a warden by, so feet on the band are in. The ring glows green, apart from the wardens’ own colours, in every phase that reads it: while the wardens gather, at dawn and in a breather after a wave. While it glows, the ring at her own feet dims on her own page, so the green one is the brightest ring she sees. The ground inside it glows faintly, the ring flares while her own character stands in it, and it pulses bright during the countdown. A warden who stands in it without being ready, as one already inside as a breather opens, reads “You are in the ring” and steps out and back in, or presses R. The following rules start a run, before the first and after each end, and go on into Endless at dawn:

  • Ready. A warden who steps into the ring is ready, and stepping out cancels it. R readies her or cancels it wherever she stands. Whichever of the two she used last stands, so a warden who pressed R stays ready as she walks through the ring.
  • The countdown. Once every warden in the room is ready, the run counts down 5 seconds, and it stops as soon as any warden is not ready, such as one who steps out of the ring or joins the room. A warden alone sees “Starting in 5” as she steps in. No timer starts a run while a warden is still gathering.
  • Start without. Once the ready wardens have waited 30 seconds on the rest, each of them sees Start without and the names of those not ready. Any of them may press it, or Enter, and the run starts at once with every warden in the room in it, the ones left behind at their places by the fire.
  • A dropped warden. A warden whose player’s connection dropped counts neither as ready nor as waited for.
  • The element. While the wardens gather, each picks an element from three cards in the middle of the screen with the 1, 2 or 3 key, or a click when the cursor is free. The pick then folds to the element’s badge along the bottom, and C opens the three cards again to pick another until the run starts. A warden who picked none is given one at random as the run starts. After a run ends, each warden gathers with the first element they held, at its first level, picked already.

The banner at the top of the screen names the goal, “Hold the fire until dawn · 15 waves”, and says what to do, “Step into the green ring by the fire”, whom the run waits for, and the countdown. The scoreboard marks each warden who is ready. Pressing Play only captures the cursor; it readies nobody.

A player who opens the page while a run is going joins it at once, as in Deep Rock Galactic and Risk of Rain 2, rather than waiting for the next as a Roblox round game holds her:

  • Her cards. She first picks her element, then is dealt a card for each breather dealt before she came, the one being dealt as she joins among them, one offer after another, and takes each with 1, 2 or 3 during a wave as well as a breather. A breather that comes while she is still taking them adds its card after hers. Past the tenth wave, her second element is among them.
  • Her shelter. She appears at her place by the fire, sheltered: no monster chases her, nothing hurts her, and she fires nothing, and the room refuses any shot she sends. The scoreboard marks her as joining. The shelter ends once she has taken her last card, or after 20 seconds, when the room takes each card she has left for her at random, as a breather that closes takes one a warden left.
  • The wave. A warden who joins during a wave grows it: the monsters still to spawn become what the wave would have planned for the new count, less those already spawned. A warden who leaves changes nothing. A colossus keeps the health it rose with.

A player who joins before the first breather has dealt, or during the first wave, picks her element alone, sheltered while she picks it.

The room keeps a dropped player’s warden for 60 seconds, standing still where she was, as Multiplayer says. Her page tries the room again each second, and on its return she is her own warden again, with her health, cards, coins, kills and colour. A reload of the page in the same tab does the same. Past 60 seconds her warden leaves the room.

While her connection is down, no monster chases or hurts her, she gets nobody up and draws no rift to her, and the scoreboard marks her as away. She still earns her share of every coin. The run ends once every warden whose player is connected is down, and while no player at all is connected, the run holds where it stands: no countdown runs and no monster spawns.

A room holds four wardens, one per colour and place by the fire. A dropped warden keeps her place while the room keeps her. A fifth player sees “This game is full (4 of 4)”.

A deployed room ends itself a minute after its last player leaves. A dev room runs until it is stopped.

Wave w against n wardens sends round((8 + 4w) × c) monsters, where the crowd c is 0.8 for one warden, 1.4 for two and 1 + 0.6(n − 1) for three or four, and n counts every warden the room keeps, a dropped one included while it holds her place. Each has 1 + 0.035(w − 1) times its health up to wave 10, and 10% more for each wave after it; 1 + 0.025(w − 1) times its speed; and 1 + 0.04(w − 1) times its damage. A batch of round((2 + floor(w / 4)) × max(1, c)) spawns every max(1.8, 3.4 − 0.1w) seconds, so a wave’s fight lasts about as long for four wardens as for one, and no more than 40 stand at once, or 60 in a Horde.

The health curve keeps a husk at two to four blaster hits through wave 10 for a warden whose cards are average picks, so the pressure comes from the count, the new kinds and the elites, as in Vampire Survivors and Risk of Rain. Past wave 10 health compounds, as in Call of Duty Zombies, so the late waves feel different.

Monster First wave Health Speed m/s Damage Seconds between hits Coins
Husk 1 30 3 5 1 1
Skitter 3 14 5.4 5 0.6 1
Brute 5 160 2.1 25 1.6 6
Spitter 4 24 2.6 12 2.6 2
Colossus Every fifth 2100 2.3 30 4 30

A warden runs at 5 m/s, so a skitter outruns her. Each monster walks straight at the nearest standing warden, blocks the wardens, and hits the one in its reach. A monster ignores a downed warden.

Five of the night’s fifteen waves carry a name: one of waves 3 and 4, two of waves 6 to 9, and two of waves 11 to 14, never two in a row and never a colossus’s wave. The first is a Swarm or a Horde, since the brutes come with the first colossus, and a Swarm when it falls on wave 3, so no Horde floods a player still learning to aim, and each of the four names comes at least once a night, never twice running. A new run draws a new night from its seed. In Endless, the second and fourth wave of every five carry a name.

The banner names a named wave, or a colossus’s, a breather ahead (“Next: Brutes”), with a line on how to fight it, and the call as it rises names it again in its colour. A named wave reshapes the plain wave’s count c:

Name What it sends Colour
Swarm 1.5c skitters, in batches twice as large and a quarter more often Lime
Brutes 0.3c brutes, at least three, in batches half as large and half again as far apart Orange
Spitter rain c monsters, seven in ten spitters and the rest husks Green
Horde 2.2c husks at three fifths of their health, in batches three times as large at the fastest pace, with no elites, and up to 60 standing at once Violet

The Horde’s 60 is one number for the room, whoever is in it. On a desktop with four wardens, a page holding 60 monsters as they die and rise again draws a frame in about 12.5 ms on average, and the late plain waves draw in 12 to 14 ms. At 90 the page draws in about 20 ms, past a 60 fps frame, so the cap stays at 60.

Every fifth wave, a colossus rises first, and round(0.6 × count) monsters come with it, where count is the usual count for that wave. It has 2100 × (1 + 0.75(n − 1)) times the wave’s health, since every warden shoots the one boss, and the HUD shows its health in a bar at the top of the screen. The night’s last colossus, at wave 15, stands 30% larger with half as much health again, and round(0.8 × count) monsters come with it. It has no claws. When a standing warden comes within 4.5 m, it stops and winds up for 1.3 seconds while a ring fills in on the ground round it, then slams: every standing warden within 5.5 m takes its damage. It slams at most once every 4 seconds.

A spitter stops 11 m from the nearest standing warden where it sees her, with no stone, hearth or ground between its middle and hers, and walks on where it does not. Every 2.6 seconds while it sees her within 16 m, it spits a glowing bolt at where she stands. The bolt flies at 7 m/s at the place she stood, so a warden who steps aside is missed. After any blow a warden has half a second in which no claw or bolt hurts her; a slam still does, and starts her half second again. A held wave takes every bolt still flying away.

From wave 6, a monster other than the colossus may rise as an elite, one in eight at wave 6 and three in a hundred more each wave after, to two in five. An elite stands a little larger and wears its modifier’s colour:

  • Swift, cyan: 45% faster.
  • Armoured, gold: two and a half times the health, and 15% slower.
  • Splitting, magenta: bursts into two skitters when it falls.

An elite drops three times its coins.

Each monster has one weak spot, and a hit there deals twice the gun’s damage, three times from a rail at Deadeye: the head of a husk, a spitter, a skitter and a brute, and the colossus’s core, which glows and throbs on its chest. A blaster shot deals 20 there rather than 10, so a wave-1 husk falls to two head shots rather than three body shots. An elite’s weak spot grows with it, and so does the last colossus’s.

The element a hit carries is not doubled: a weak spot lays the same dose of Storm, Ember or Frost as the body, so each element stays as strong a second through every gun.

Marksman adds a share to each weak-spot hit of the warden who holds it: 25% of the hit for a common, so a blaster head shot deals 25 rather than 20. Every gun and the lance lay it on their weak-spot multiplier, the engine’s zone damage, and the rail’s Deadeye tier lays half as much again, so the room deals the whole of a head shot in the shot’s one hit and the number that floats off the monster shows all of it. A Deadeye rail with a common Marksman deals 3.75 times its damage on a head.

The room runs no animation, so each weak spot is a sphere or a capsule fixed in the monster’s own frame, turning with it, sized to hold the head or the core through the walk, the strike and the flinch. The room judges it where the monster stood and faced on the shooter’s screen, as it judges the hit. Weapon says how.

A warden at no health goes down, as Left 4 Dead’s incapacitated survivors and Borderlands’ Fight For Your Life keep a downed player in the fight. Her health lives on her warden’s own trait rather than the engine’s, so the engine never downs her, and the scene’s <CharacterSpawn> turns the engine’s respawn off as well: only the game’s revive gets her up. She cannot walk, the monsters leave her, and she keeps firing where she lies at half her rate with each weapon: the room halves her rate stats while she is down and reads them as it judges each shot, so a page that fires faster is refused. A teammate who stands within 2.2 m of her for 3 seconds gets her up with 40% of her health. Holding the wave gets her up too. A warden who gets up from a fall takes no damage for 2 seconds, from a claw, a bolt or a slam, as Risk of Rain 2’s Dio’s Best Friend grants after its revive: the monsters crowd a downed warden, and without it every one strikes as she stands.

A warden alone, the only one whose player is connected, gets herself up once each boss cycle: down, she counts her own revive up over 5 seconds while she keeps firing, and gets up with 40% of her health, and the run does not end while she does. The self-revive comes back when a colossus’s wave is held, and a run starts with it. A second fall in the same cycle ends the run. With two connected wardens or more, nobody gets herself up.

The map is a 100 m square. Its middle is a level meadow, 32 m out from the fire, rising over 12 m more into broad grassy hills, with a cobbled ring at 14 m, flagstones round the fire, and four dirt roads that leave through low passes in the hills to the forest. Ten standing stones stand at 19.5 m, their runes blue between waves and red during one. The game draws its own ground over the engine’s.

The engine’s walls stand on the square’s sides, and the game dresses every one of them, so a warden sees what stops her: a broken row of young firs on the wall’s line with a taller row behind, and deadfall along it, bare logs, fallen firs, boulders and stumps. Where each road meets the wall, a windthrown fir lies across it behind a log, its root plate torn up on edge beside the road. Past the wall the ground runs on in the same paint into low wooded hills, and each road runs on as a track that bends away among the trees, so no view from the circle finds a drop or the dark under the skyline. The land, the wood on it and the thicket are drawn only, with no body; the walls are the boundary.

The circle opens at dusk under blue fog. A fir forest stands round the map’s edge in stands that rise and dip, so its line against the sky is ragged rather than level, and past it a skyline of three ranges: far peaks, a nearer ridge and a line of firs, each darker than the one behind it, in the sky’s own colors and hazed toward its feet, so each road leads the eye out to mountains at every hour of the night. The hearth and the stones’ runes are the brightest things on the map, each with a pool of light on the ground at its foot.

The sky turns with the run, a wave at a time, easing through the opening seconds of each breather: dusk at wave 1, with the sun low in the west and the first stars; nightfall by the first colossus, with the moon over the east and the whole sky of stars; the deep night through the middle waves, lit by the moon and by the fire, whose light falls off slowly enough to reach the ring round it; the east paling after the second colossus; the sun on the eastern horizon at wave 15; and the sun up at dawn and climbing through Endless to a clear morning. One list of stops sets the engine’s hour, which places the sun and the moon and lights the circle, the game’s own sky, fog, skyline and side light, and the look’s grade: a warm tint at dusk and dawn, and a cool, slightly drained one at midnight. The engine’s full dark sits between the two sides of the night, so the hour steps across it at midnight while the moon is low, and the circle never falls under two thirds of dusk’s exposure. The end screen leaves the sky where the run fell, and a new run turns it back to dusk. The runes, the fire’s warmth, the sky and the red edge of low health ease toward the run as it moves, and stand on it at once where a page joins, rejoins or a replay seeks, so a shot of a held replay shows the moment as it was.

Each warden wears one of the platform’s two VRM avatars, in turn by join order, and plays the engine’s clips. A ring of her colour at her feet tells the players apart across the circle. Her right arm is held out along her aim, with her gun from Kenney’s Blaster Kit in her hand: glowing rings at its muzzles heat up as she fires, it kicks as hard as it is heavy, and a band of light round its barrel for each tier says how far she raised it, gold at the third, and flares as she buys it. A gun she takes up swings up into her hands. A downed warden lies on her back with her feet toward her aim, along the ground’s slope where she fell, sits up from the hips, and holds the blaster out over her feet. A monster that falls while a player watches plays its fall and sinks into the ground; one that a rejoin or a replay’s seek leaves out is gone at once.

Each kind is a rigged model by Quaternius: the husk a zombie, the skitter a spider, and the brute a demon, drawn without the halo its file floats over its horns. The spitter is the zombie in green, and the colossus the demon at 4.2 m, charred dark with a net of molten orange seams that burn hotter as it winds up its slam, and a longer, slower stride than the brute’s. Each plays clips to walk, strike and die, and the husk and the brute flinch when hit. A monster with a warden in its reach winds up its strike and throbs in its colour before the blow lands. A hit flashes it white; a Storm hit or arc lights its outline alone, so the lightning stays the brightest thing on it. Every monster keeps a thin rim of its kind’s colour round its outline, stronger the farther it stands, to about two and a half times its strength at 26 m, so a husk across the circle stands off the night’s grass. A monster climbs out of its rift as it spawns, the colossus over 1.6 seconds rather than 0.6 with a shake, a flash of orange light and thunder, and one that stood already as a page joined, rejoined or opened a replay stands up at once. The colossus, which every warden fires at, takes hits faster than a flash fades, so it keeps its colour and a hit lights its outline instead. Its core glows on its chest in its eyes’ colour, riding its chest as it walks and swings, and throbs about once a second.

Every monster but the colossus draws in its model’s skinned batch: the bodies of every husk and spitter standing or falling are one draw into the frame and one into the sun’s shadow map, each posed by its own clips and lit by its own flash and wind-up, and so are the skitters’ and each of the brute’s five meshes. The colossus, the night’s boss, draws on its own. What a monster draws beside its body is one instanced draw of each kind for every monster: its shadow on the ground, the glow of a husk’s eyes, the ring its wind-up marks, its health bar, and what the elements leave on it, a mark’s ring, a burn’s tongues of flame in three shapes, the frost on the ground and a frozen monster’s ice. A chill’s frost on the body is the monster’s own skin, lit by its view.