Public arena

Fragyard is a browser deathmatch in a raycaster with a Wolfenstein 3D core and thirty years of additions on top. You can customize your character, join an arena, and frag your friends. Networking is peer to peer over WebRTC. You simply share a link and your friends join the game in the same room. Pick a mode: deathmatch, team deathmatch, gun game, or last man standing, and go.

The inspiration

The renderer leans on the same tech that powered Wolfenstein 3D: a grid of walls, a fan of rays, and a vertical strip of pixels for each one. I built Fragyard as a tribute to the 90s and to the gaming spirit of that era, when a game was one clear idea made with obvious love. The modern features I added to the graphics tech sit on top of that foundation rather than burying it.

Mostly I wanted to make something fun and simple that could grow into multiplayer without a server behind it. Fragyard runs on a serverless, peer to peer architecture, so players jump straight into a match with nothing to download and nothing to install.

Player made content

Fragyard ships with browser tools for building your own maps and characters, and you can find them in the Tools tab. They run entirely in your browser, so nothing you make is uploaded anywhere. The tooling is still growing and I am working on making it cover more of what the game loads.

Player made content is welcome. If you build a map or a character you are happy with and wish to share with others, talk to me on discord, and I will look into adding it to the next game build. Creators whose work ships in a release get their name added in the credits screen.

Support the project

Fragyard is free to play and my current plan is for it to stay that way. If you enjoy it, please consider sponsoring the project - monthly or a one off, whatever suits you. Support helps me keep working on it, adding new features, and keeping the game free for everyone.

About the author

To see more of my work, and read my rumblings on graphics, rendering, and other topics visit my website at 4rknova.com.

Thank you playing, and see you in the arena.

Nikos Papadopoulos

These are the features I plan to build, grouped by topic. Think of it as a direction, not a schedule: nothing here has a date, priorities shift as the game grows, and the list will change as items ship, split, or get rethought.

Bug Fixes0 of 2 shipped

  • Projectiles travelling through portalsIf direction before and after is not the same relative to the player, the wrong sprite angle is rendered.
  • Rockets explode at ceiling levelA special path is needed at open ceilings.

Engine0 of 7 shipped

  • Maps that count, wait and decideAn arena can already react: step somewhere and a door opens, a prop breaks, a sound plays, the music changes.

    What it cannot do yet is count, wait, or ask a question first. Three players standing on the plate, ten seconds after the round starts, only while your flag is still home. That is the difference between a map that responds to you and a map that runs something.

  • Doorways that do not always lead the same placeA lift or a passage arrives where its map says it arrives, every time.

    Letting one pick from a set of exits, or choose on the state of the round, turns a fixed arena into one that can rearrange itself.

  • Walls you can use, not just breakSwitches and moving panels.

    Doors are built already: six types, each taking its opening direction from the map, and a type can be see-through.

  • Low wallsA wall that stops at knee height is scenery -- kerbs, planters, counters.

    One that stops at chest height is cover, and cover is the thing that separates what you can see from where you can walk.

  • Cutscenescamera paths, and 2D storyboards with a parallax effect.
  • Net code and protocol optimisation
  • Documentation

Graphics0 of 4 shipped

  • Palette cycling water fountains
  • The distance fade, drawn the way 1993 drew itFar walls already darken smoothly.

    Doing it as an ordered dither instead reads as deliberate where a smooth gradient reads as modern.

  • Shots that light the room as they travelA muzzle flash already throws light.

    Carrying that light down the path of the round would make sustained fire strobe the walls, which matters most in an arena whose lights have been shot out.

  • Screens that show somewhere elseA wall can already be a screen.

    Feeding one a live view from another part of the arena turns a wall into intelligence, gives a defender something worth standing near, and makes shooting out a camera -- or the screen it feeds -- a real play.

Game modes0 of 3 shipped

  • AssaultOne side attacks, the other holds.
  • The collapseThe arena closes in as the match runs: doors seal a wing, passages go dead, the lights drop section by section.

    Not a circle on a map -- a sequence the mapper writes, so every arena collapses its own way.

  • ImpostorOne player is randomly selected to be the assasin.

    Kill all others before being discovered.

Weapons and loadouts0 of 4 shipped

  • Flare gunA round that sticks where it lands and burns, lighting the space around it for a while.

    The tension is in the weapon rather than bolted on: it shows you the room and shows you to the room.

  • RailgunPunches through walls, leaving a hole where it went in and another where it came out.
  • EMPKills the lights in a radius for a few seconds and hurts nobody.

    The answer to a flare, and the aggressive version of a blackout.

  • Take down from behind with knife

Skills0 of 5 shipped

  • X-Ray visionSee through walls
  • InvisibilityCancels if you shoot / attack
  • Teleportationmark a location, teleport on command to that location
  • DeflectBeing hit you take 75% of the damage and the shooter gets 25%
  • Regenerate HealthAt fixed rate for a specific time period

Controls and accessibility0 of 1 shipped

  • Rebindable controlsThe movement keys cannot be changed.

    An AZERTY keyboard, a left-handed player, or anyone who cannot comfortably reach that corner of the board is locked out rather than inconvenienced. It is the cheapest item here measured against who it excludes.

Art and audio0 of 3 shipped

  • Real sound for everything that still plays a placeholder toneThirty-three of the game's sixty-two gameplay sounds are synthesized stand-ins: all twelve door clips, the drone's five, the four reload clips, the rifle's shot, the flamethrower's impact, the missile launch, fire, breaking glass, breaking props, stepping through a portal, a body catching fire and going out, and the three pickups.

    The sentry, the axe and the boot screen have real recordings now.

  • The sounds nothing asks for yetProps that break differently depending on what they are made of, a fuse hissing on a barrel before it goes, the charge and wind-up sounds no weapon has, and a pickup that anyone but the taker can hear.
  • Props that look like they belong to the same gameThe library is thirty-five sprites, and twenty-five of them are now drawn by hand against one palette rather than by three owners who did not agree with each other.

    Two of the four stages are done: the shared palette, and the re-draw of the flat props. What is left is props that animate and break over more than two frames, and props that look different from every angle instead of the same from all of them.

Creation tools0 of 5 shipped

  • Wall materials that carry a live valueA material's yml can already declare a text: block naming a game value -- $survival_wave, $total_kills -- and the wall editor writes one.

    Nothing reads it yet: the runtime substitution is sub-project B, a generic display painter that replaces the hand-coded arenaname painter in src/game/art/walldisplays.js with data.

  • Readable names for sounds in map rulesA rule that plays a sound names the whole file path today, so a map cannot be read without a path in every line.
  • A rule that names something the game does not have should say so in the listIt reads as fine until you open it, and the list is where a mapper looks first.
  • Two editor annoyances worth fixingTyping into a rule field can swallow the click you make next, so the first click after typing sometimes does nothing.

    And one rule row reads with a stutter, repeating a word it has already shown.

  • The editor can strand a map in a state only the raw text pane can fixBoth cases are the same shape: a floor name the cell menu will no longer offer to clear.

Browser tools for building things for Fragyard. They run entirely in your browser; nothing you make is uploaded anywhere.

Map EditorDraw an arena grid, place spawns, ammo pads and props, and export a map yml.opens in a new tab Character EditorOpen a character and paint its recolor mask, tuned against a live preview, then export the files.opens in a new tab Wall EditorLayer procedural patterns into a wall material, check the tiling live, edit its pixels, and export the files.opens in a new tab Weapon EditorTune a weapon's numbers against a live viewmodel that fires on the game's own clock, and export a weapon yml that keeps every hand-written comment.opens in a new tab Parallax EditorDrag a menu backdrop's layers into place over the renderer that draws them, with the scene's own rules reported as they move.opens in a new tab Prop EditorPaint a prop frame by frame, say which frames a break plays, and watch it stand on a lit stage that breaks on the game's own clock.opens in a new tab

Every notable change to Fragyard, newest first. Each entry is one release. Pick one from the rail to read it; the sections of the release you are reading are listed underneath it there.

0.9.3

walls you can pull, a sky with things crossing it, and distance that fades to the horizon.

0.9.2 rebuilt the room around a respawn. 0.9.3 is about everything in an arena that is neither a wall nor a player. There are levers on the walls now, and pulling one is a thing you do with a key of its own -- bots pull them too. The sky above an open arena has birds, airships, satellites and meteors crossing it, and the birds roosting on a parapet scatter when someone fires nearby, so a flock going up is information. Underneath both, distance stopped fading to black: an outdoor wall now fades toward the colour of the sky behind it, over a reach scaled to how far the arena can actually be seen across, which is the single change that most alters what an open arena looks like.

Switches

  • Walls you can use. A map may hang a switch on a wall face -- a lever, a button or a keypad -- and pressing the use key works it. Each kind has its own reach, its own facing cone, its own label, its own sound, and its own idea of whether it is held down or toggled, so a keypad you tap and a lever you pull are not the same gesture.
  • The game tells you it is there. Standing in reach and facing it draws a [C] PULL LEVER prompt on the HUD, with a progress bar for a switch that wants holding; on a touch screen a USE button appears in the same moment and stays hidden the rest of the time.
  • A switch raises an event and the map's rules do the rest. It is the same vocabulary a pressure plate or a zone already speaks, so anything a rule can already do -- open a door, lock one, start something -- a switch can now be wired to.
  • Bots work them too. A bot, a survival wave enemy or an escort sentry that walks past a switch in reach will work it, with a wide facing cone so it does not need to line itself up first. A switch is part of the arena for everyone in it, not a control panel only the player can reach.
  • HIVE gets a lever. It hangs inside the ops room on the west wall, over the service passage, and it is only reachable from inside -- so it cannot get you in the first time, which is still the plate's job. What it changes is what happens after: pull it once and that door stays open for the rest of the match, so a squad that has fought its way in can leave itself a permanent back way instead of re-fighting the plate every time.
  • A switch survives a bad connection. Its state travels as a mask rather than as the moment it flipped, and the referee re-sends it twice a second, so a peer that misses the press converges within one interval instead of holding the wrong world forever -- and serving that wrong world to the next joiner.

Doors

  • A door only a rule opens. A door whose trigger is zero ignores anybody walking up to it: nothing but a rule moves it, which is what a switch on the far side of the room is for.
  • A shutter can hang in an open span. A map may name a door's axis, so a door no longer has to be framed by jambs on exactly one side to be placed -- a roller can come down across a wide opening or a four-way crossing.
  • A door says what it does to a body in its cell. A closing door pushes, or crushes, or refuses to close at all, and the door type declares which. Blast doors refuse, which is what they have always done in practice; the field just says so now rather than the behaviour being hard-coded for every door alike.
  • A crush no longer freezes the door it kills you in.
  • Every door type is now in an arena. Mallglass was the one of six that no map had ever bound -- it shipped a texture and two clips no player had met. It goes at HIGH STREET's north-east alley mouth, answering the shopshutter across the lane: the shutter is opaque and hides whoever is behind it, the glass does not, and a spawn you cannot see into belongs to whoever is camping it.

The sky

  • The sky has things in it. Seven kinds of them: flocks of birds, raptors circling on a thermal, high contrails that linger behind the aircraft that drew them, airships, satellites tracking over, meteors streaking on the weather's own schedule, and plumes of smoke shearing off in the arena's wind. Every shipped sky is dressed, and dressed low enough to be seen from the default pitch rather than only if you look up.
  • Birds roost on the arena and a shot puts them up. A roosting parapet scatters into an airborne flock when someone fires nearby, visible across the whole arena -- so a flock going up tells you somebody is shooting over there even if you never heard it. Deathmatch and last man standing are loud enough to trigger it; gun game and survival turn it off, since in those a shot is constant and would say nothing. The site quietly repopulates half a minute later.
  • Clouds drift. A sky may carry a second, transparent deck that slides across the one behind it -- about ninety seconds for a cloud to cross the frame, so it reads as weather rather than as a texture being moved. The noon sky gets one.
  • A map dresses its own sky. An arena may replace the whole list its sky suggests, or write dressing: none and have an empty one.

Distance and fog

  • Distance stops fading to black. Fading toward black is right indoors and backwards outdoors: under an overcast dome, a wall past the arena's fog reach sat at the brightness floor while the sky directly above it was near white, so the roofline read as a black band rather than as distance. An outdoor wall now fades toward the colour of the sky behind it. Indoor arenas are untouched -- with no sky to fade toward, the maths is the same maths as before.
  • An open arena is fogged as an open arena. Fog reach defaulted to ten cells, which is a corridor's number, and every sky-open arena but the two whose authors had picked a reach by hand was rendering an outdoor space as though it were a hallway. A sky-open arena now takes its own grid diagonal -- the longest sight line it offers -- so full fog lands about at the far corner rather than a third of the way across.
  • The falloff has a shape. The ramp was linear, which spends its darkening where the player is actually looking. A curve above one holds the near and middle field bright and puts the fall at the far end. One is the shipped ramp, so nothing re-shades by default.
  • A mapper tunes an arena's fog. A map's fog: may now be a block setting the reach, the curve, the strength, the brightness floor and the colour, over defaults that moved out of the renderer and into game.yml where they can be read and changed. The older fog: 55 scalar still means the reach and nothing else. An authored colour beats the sky the game would have sampled, so an arena can be lit for a mood its dome does not have.
  • The map editor authors it. An arena built in the editor carries its fog, and can set it and look at the result without leaving the browser.

Arena boards

  • The walls read out the match. A screen on an arena's wall used to show one thing, the arena's own name. A mapper can now point any of them at the match instead: the score, the clock, who is winning and by how much, the round, the last frag, how many are left alive, which wave is coming, or your own rank and health. Twenty-six of them, and a wall says its piece to whoever walks past -- so the room tells you where the match stands without taking your eyes off it for a scoreboard.
  • A board reads from every side. It is one panel on the whole cell, so it says the same thing from all four faces and at every floor of a tall one, rather than being a sign you have to find the front of.
  • A board that has nothing to say says so. A team score in a deathmatch shows a dash, not a nought -- no team game rather than a tied one.
  • Mappers put a sign on a wall. board=<source> on an arenaboard cell picks what it reads out, right-click in the map editor to choose one, and board=custom shows free text the map writes in its own boards: block -- thirty characters over three rows, so an arena can name its own rooms.

Litter, acid and fumes

  • Litter is paper, not white squares. Wind-borne scraps were a bare rectangle filled toward a fixed pale tone, which at six pixels reads as a white square -- and out where the fog had taken the floor to almost nothing, a scrap sat seventeen times brighter than the ground it was blowing over, glowing in the dark distance. A scrap is now torn paper or a dry leaf off an authored sheet, shaded by the same fog and the same lightmap as the floor under it.
  • Acid does not draw fire. Standing in slime lit the same full-screen burn a flamethrower does, so wading a channel put a wall of orange across the view of a player who was, in fact, in acid. Two green passes take those frames instead: a dithered haze for the gas and thrown gobs of splatter for the liquid.
  • Slime sounds like slime. Standing in a caustic floor was silent, while costing ten health a second. It now holds a bed of its own, declared on the material rather than in a table somewhere, so the next caustic floor anybody authors sounds like whatever it is instead of like this one.
  • A vent's gas is drawn rather than generated. The procedural puff was five lobed blobs and read as foliage; the authored strip reads as gas.

Props and the map editor

  • A fountain for the village square. Sixteen frames of it: the jet climbs out of the spout, peaks into a plume, falls back in arcs that splash into the basin, and the water settles to still before it starts again.
  • A prop stands on a cell instead of being one. Props used to occupy a cell in the grid, which meant a prop and the floor it stands on could not both be said. They now live in a sparse layer beside the grid, with a rail of their own in the editor: place, rotate through the quarter turns with O, and Shift-drag to erase.
  • Painting over something works the way painting works. A drag on the Floor, Sky or Height layer used to be refused if it overlapped an entry already there -- so correcting a floor meant erasing the rectangle and then dragging the same rectangle again. A drag now paints over what is under it, within its own rectangle and no further, and one undo takes the whole thing back.
  • A floor or a sky rectangle may cover a wall. It used to be refused outright, so one wall clipped by a rectangle refused every open cell in it too. It is allowed now -- and it matters most under a see-through cell, where a ray marches through a chainlink fence or a glazed facade and reaches the floor beneath, which until now could not be chosen. A fence takes a floor and a sky at once.
  • Eight portal pairs instead of four. A multi-floor arena ran out, since every lift wants a pair of its own; lime, cyan, violet and magenta join the original four.
  • The editor stops eating things it never loaded. A map's weapon crates and its decals both survived a round trip through the editor only by accident before, because the editor never loaded the registries that name them.

Options

  • One row for how the game fills the screen. OPTIONS > GRAPHICS used to spend two rows on it -- a DISPLAY toggle for the in-page look and a separate OS FULLSCREEN action -- and the two could disagree. They are now a single DISPLAY row stepping WINDOWED, SCALED and FULLSCREEN: the small centred picture, the same picture grown to fill the browser window, and the browser's own fullscreen with the chrome gone.
  • The row says what is actually on screen. FULLSCREEN is the browser's state rather than a saved setting, so leaving it with Esc or F11 puts the row back on whichever of the other two was saved, instead of claiming a fullscreen that ended without asking. Stepping off it returns to that saved mode.
  • Where a browser has no fullscreen to give -- Safari on an iPhone offers it to video and nothing else -- the row stops at SCALED rather than offering a step that would do nothing. Installing to the home screen is still the way to lose the address bar there.

Roster

  • The roster says which match it is. Holding Tab now names the arena and the mode across the top of the panel, above the players -- so a joiner who came in through an invite link, and never saw a map picker, can find out where they are and what the rules are without leaving the fight.

Weapons and the view

  • A run drops the gun. The weapon already swung wider when you ran; it now lowers as well, riding the same swing, so the rest pose every authored weapon is measured against does not move and only the run changes. An aimed run damps it with the sights, so a running player is not left sitting under their own reticle.
  • A spent throwable leaves your hands when its throw stops drawing, rather than lingering as a held object with nothing left in it.

Elsewhere

  • Flags belong to capture. Every arena that marks flag bases also offers deathmatch, and a deathmatch on one of them spawned both flags, drew them at full size, put them on the minimap and sent half the bot roster off to run them -- for a score no mode but capture keeps. That was a third of the map rail. The flags now come with the mode that wants them.
  • The match clock counts up through live play.
  • Travelator surfaces. Six cycling floor materials in three looks, each with the return twin a two-way walkway needs -- meant to be stood on rather than looked at.
  • A console line that overruns the panel folds instead of running off the edge and losing the rest of itself, which is why every command that had something long to say used to say it in short pre-broken pieces.
  • On the front page, a press of Watch lands the first time. The live widget redraws on every wire event, and a redraw inside the tenth of a second a finger is down retargeted the press to a row where nothing listens: a quarter of all presses went that way, with no error anywhere. Aiming the feed's camera by clicking a name failed just as often.

0.9.2

the arena builds itself around you, bots learn what a door is for, and the backdrops get an editor.

0.9.1 filled the menu in. 0.9.2 is mostly about the moment you come back. A respawn is no longer a blue wash over a room that was already standing there: the place assembles itself around you, edges first and near before far, and it is finished before you are shootable. Behind it, the AI finally understands a doorway -- until now a shut door was solid rock to every bot and every survival wave, so any arena with a door in it had rooms the enemy could not enter. And the parallax backdrops the shell stands in front of get a browser editor of their own, the sixth tool shipped with the site.

Doors

  • Bots and enemies walk through doors. A door only ever opened for the player, and a shut one counted as solid rock when a bot worked out where to go -- so no route through any doorway existed, and a bot that wanted to reach you took the straight line instead and spent the rest of the match pressed against the nearest wall. Every arena with a door in it had rooms the AI could not enter. Now a body plans through a doorway the way you do, and the door opens for whoever walks up to it: bots, the survival waves, and the escort machines that follow you.
  • A locked door still stops them. A door a map's rules have shut is not a way through for anybody, so nothing plans a route through one -- which is the point of locking it.

The shell's backdrops

  • Eight places leave the library. The shell stands in front of eight now -- a street, a cable car, a car park, a dry dock, the Parthenon, a station, a supermarket and a train shed -- where it carried sixteen. The eight that go stood behind no screen: every screen that names a backdrop names one that stays, so nothing in the menu looks any different. What changes is the download, which is 4.6MB lighter for art nobody was being shown.

Respawning

  • The arena builds itself around you when you spawn. Coming back used to be a blue wash over a world that was already finished and standing there. Now the room arrives: its edges strike in first as gold lines, near ones before far, drawing the plan of the place you are standing in -- the cell grid across the floor and ceiling, the corners of every wall, the outline of anything in the room with you -- and then the surfaces flood in behind them in the same order, crumbling in through a stipple rather than sliding in under an edge. It takes a little over half a second and it is over before you are shootable.
  • Whatever can reach you is drawn first. The build is ordered by distance, so the near half of the room is solid while the far end is still a wireframe, and a body standing in front of you is outlined from the very first frame -- an enemy is never hidden by the effect that is drawing the room they are in.
  • Arriving has a sound. It plays from the same instant the picture starts and runs the length of it.
  • The blue flash keeps its other job. It was saying two things at once: you have just arrived, and you cannot be hurt yet. The first is now the room's to say, so the wash holds off until the room has finished and spends the rest of the protection as the quiet pulse it always settled into.
  • spawn.reveal in game-data/config/game.yml sets how long it takes, and colors.overlay.constructLine what colour it draws in. 0 turns it off.

Tools

  • A parallax editor. A sixth browser tool, at /tools/parallax-editor.html, for the backdrops the menu stands in front of. Its layers are dragged over the renderer the shell actually draws them with, and the two rules a scene has to keep -- a plate wide enough to cover the frame at both extremes of its drift, and blur that never increases toward the back -- are reported while a number moves rather than by a test long after. Sweep runs the aim to both limits, which is the only way to see a plate that is too narrow.
  • The menu's own furniture draws over the art, read from the constants menu.js exports rather than copied, so it follows when a menu row moves: the scrim, the rows, the value column, and the box the forest's cabin aims its lit window into.
  • A character can be stood or set pacing anywhere in the scene, and standing is a pose the format states now rather than one it fell into by accident.
  • Erase and pick repair what a chroma key left behind on a plate, on one undo stack that holds brush strokes and typed numbers together -- an author does not remember which of their last six actions was which.
  • New starts a scene from loose plates, in the order the cutter would have put them in. Export writes an <id>.asset that npm run asset -- import installs, checked on the way in like every other kind of asset.
  • Saving splices the values that changed into the lines the old ones occupied, so every measured comment in a scene.yml survives being edited by a tool.

The dev console

  • die [gib] [head] kills you on the spot, so the death presentation -- the fall, the corpse, the loot drop, the kill feed line, the respawn clock -- can be looked at without arranging to be shot. gib bursts the body and head marks the headshot, two things that otherwise need someone else's explosion or someone else's aim. Nobody is credited the frag, survival still turns it into a down while a squadmate is standing to pick you up, and it beats god, because an order to die is not damage. Like every cheat it refuses in a room with another person in it; a bot room -- the one you want when you are looking at your own corpse -- runs it.

The site

  • The release notes page stops scrolling sideways on a phone. Under the two-column breakpoint the version rail lies down as a strip of chips, and the strip was making the whole document 549px wide at every viewport narrower than that, so a phone zoomed the page out to fit and put a horizontal scrollbar under it. The rail scrolls now, and the page does not.

0.9.1

the lobby keeps a list, the backdrops get weather, and a floor can kill you.

0.9 turned the menu into a place. 0.9.1 is about what is happening in it. The ROOM screen stops being a blank field with a code in it and starts showing you the rooms that are actually open, with the mode each one is playing and how many people are in it. The backdrops the shell stands in front of come alive: rain, ash, mist, a flicker in the lamps and a shimmer through the water, all of it declared by the scene rather than by the code. Your own soldier steps out of the black plate the personnel file kept him in and stands in the carpark, and when the credits finish rolling he walks in and takes the platform. And a floor can now cost you health, which is the substrate one new arena is built on.

Rooms

  • The ROOM screen lists what is open. Down the right of it is every room the lobby has heard from, with the mode it is playing and the head count in it, under a total that says how many there are. Arrows walk the list and ENTER joins the row you are on; the code field on the left is unchanged and still does what it did. An empty lobby says NO OPEN ROOMS rather than printing headings over nothing.

Trystero cannot be asked what rooms exist, so a listed room announces itself instead: its referee re-broadcasts an advert into the lobby every five seconds, and a room drops off the list twenty seconds after the last one. Several periods rather than one, so a single dropped packet does not blink a live room out of the list. A room is keyed by its mode and code rather than by the peer that advertised it, which is what makes a referee handover invisible: the new referee's advert overwrites the entry in place, and the old one's eventual leave finds an entry it no longer owns and leaves it alone.

The table itself is pure and has no mesh and no clock in it, because every interesting case here is a race between an advert, a leave and the clock, and its tests drive all three by hand rather than against a relay.

  • A room can be founded unlisted. PRIVATE on the ROOM screen keeps the room out of everybody else's list, so a code you pass to friends is the only way in. It is dim and inert until you have typed a code, because a blank code always plays the public arena and that arena is always listed -- and a blank field can no longer leave the toggle set from a code you cleared.
  • The list says what it is hiding. A list longer than the panel used to truncate in silence; the last visible row is spent on a count of the rooms that did not fit. The marker is not a row the caret may walk, so it cannot be selected and joined into nothing.
  • What arrives over the wire is bounded before it reaches the screen. The room table is pruned rather than merely filtered, and capped, so a peer flooding distinct mode and code pairs cannot grow it without limit; the mode string is capped at the boundary; the head count is clamped to the column it is drawn in rather than printed past it; and an advert naming a mode this build does not know is dropped before it can be drawn or joined into a room that cannot resolve.
  • The wire protocol steps to 34, for the advert and for the seat that sends it: a playing referee holds a second connection into the lobby to announce its room there, without being counted as a watcher.

Matches

  • A bot is a combatant, so LAST MAN STANDING enforces one life against one. Picking that mode on your own against a squad of bots gave an arena that looked normal and followed none of its own rules: everybody who died came straight back, no round ever ended, and the banner read WAITING FOR PLAYERS the whole time. The roster the phase machine runs on stripped bot ids, so a solo room counted one player against the mode's minimum of two and never left warmup -- and the mode's respawn rule is "warmup", which then answered yes forever. Nothing gates fire or damage on the live phase, so the match played perfectly normally and simply never enforced the rule it is named after.

A bot now fills a slot the mode is waiting on, holds a place in the standing count, and can be the last one standing and take the round. CO-OP is excluded and the mode declares that itself rather than the code testing an id: a squad bot is not something you are fighting, and survival reads an empty alive set as "every player is down, the run is lost", so counting the squad there would stop a run ever ending.

Maps

  • A floor can cost you health. A wall material's yml can declare a harm block -- damage a second, and a burn that outlives the cell -- and any arena laying that material as its floor charges every body standing on it: the player, the bots, and a survival wave alike. Nothing about it crosses the wire. Each client bills the bodies it already simulates, a fatal tick names the body as its own killer rather than crediting a frag to whoever happened to be nearby, and godMode is refused outright.

NUKAGE's shallow nukage is the first floor to carry one, which retires the joke it has been telling since it shipped: the pools you wade through are the reason the arena is named, and until now they were scenery with a hazard stripe painted round them. Ten health a second, and the burn they light follows you out onto the walkway for a second and a half after you have left the pool, which is the part that actually decides whether the short way was worth it.

What the burn does NOT do any more is take the legs out from under a monster. A floor bills a body every frame, and the flinch every blow in the game causes re-arms itself the frame it expires, so the first version of this froze any enemy that stepped in acid until it dissolved -- half a cell in four seconds, measured. Environmental damage now takes the health and leaves the gait alone. A blow still flinches.

  • SLUICE, a new arena, is what the caustic floor was built for. Two pump halls, north and south, joined by the works: two deep slime channels that cannot be crossed at any price, staggered so the safe route weaves the width of the arena twice, and one two-cell mouth cut through each channel with the shallow floor laid in the gap. The mouths are the short way -- about a second of wading, ten health, and a burn that follows you out the far side -- against the twenty-odd seconds the weave costs. Every harmful cell has a way round it, and the test suite refuses an arena where it does not: acid on the only route between two halves is a toll booth, not a choice.

The personnel file

  • The subject stands in the carpark rather than in a photograph of one. The PLAYER screen's picture was a black rectangle punched through the backdrop the rest of the screen stands on, with bracket corners around it, and a figure in a plate like that is a figure floating in a hole. The plate is gone: he stands on the scene's own ground, behind the rubble in the foreground, and takes that layer's drift so he travels with the deck instead of sliding across it. Which layer he stands on, where his feet land, where he stands across the frame and how far he is blown up are all declared in the scene's own file, so standing a figure in a different place is an edit to data.

The close-up went with the plate. There is no push-in on the part under the cursor any more -- the whole figure holds on every row, and the swatch beside each colour is what a part is judged against. The ledger that was under the plate moved to the top of the column beside him.

  • The file opens three-quarter, and its column gets a scrim. Square on is a mugshot: it flattens the figure and hides the depth the eight facings exist to show, so a file now opens on the front-right facing instead. The right column takes the root screen's rail scrim, mirrored -- opaque at the margin the type is set against and gone before it reaches the figure -- because that column is now printing over a lit carpark rather than over a flat field.
  • The subject is no longer judged through the veil. Every scene-backed screen lays a dark veil over the backdrop, and on this screen that included the figure, who is not backdrop here: he is what the screen is for. Every colour you picked was being judged two stops darker than the character will ever look in a match. The file takes no veil now. The perk picker still does, because it prints a twenty six row catalogue across the whole frame with no scrim under any of it.

The veil itself was broken in a quieter way as well: it was named in the config and read by the shell, but the colour table carried no default for it, and a key with no default is dropped at parse. So the value never reached the shell at all, and every scene-backed screen was veiled with whatever fill the scene had last used.

  • The screen wears the shell's own header. It printed PERSONNEL FILE against a works order number, in character, but it was the one top row in the shell that did not say which screen you were on, and its rule stopped at the margins where every other screen's runs the full width.
  • The subject glances at the camera when you leave him alone. He looks toward whoever is reading the file and back, every eleven to eighteen seconds, and any input at all -- turning him, moving the cursor, typing a hex, stepping a colour -- restarts the wait, because an idle is what happens when nobody is doing anything.

The turn takes a step. Two poses with nothing between them is a pose swap, which is what read as mechanical; each half of the gesture now borrows a walk cell for seventy milliseconds, because a standing person pivots with their feet. The two footfalls take opposite phases of the walk cycle, so the return is not the departure played backwards. That is the smallest movement this art can make and it is now measured rather than assumed: every pose in the sheet repaints between 76% and 125% of the standing figure, so there is no legs-only frame in it. A real settle is one cell per facing, thirty two across the cast, and a taller sheet.

The menu

  • The jukebox is two columns under a title bar. The screen was asking for about 205 rows of height in a box 168 deep and the track list lost: five rows were visible out of a catalogue of eighteen, and two of those printed on the footer band. The header was where the height was -- the game's name set at scale 4 in a band 50 deep, on a screen you reach by choosing JUKEBOX from a menu. It is a title bar now, the game's name as chrome on the left and the screen's own name on the right, and the list takes fourteen rows of what that gave back. How many rows fit is derived from where the list starts and where the footer rule is, rather than chosen, because a constant cannot notice that it has walked out of its band.

The three option toggles were a column of labelled rows as wide as the list, for three fixed values; they are one row of sprites with their value beside them. Repeat keeps its value in text, because it has three states and a digit inside a ring that size is not legible.

  • The MAP screen joins the same chrome. It sits between MODE and BOTS in the flow, and a header a different depth from theirs reads as the shell lurching. The bands and their depths live in one place now, which is the rule that matters: a depth may not be written down twice, and the map screen was already a third copy of the two numbers.
  • The ROOM screen's two columns stand on one grid. The left column's blocks sat at offsets of their own, so the screen read as two unrelated halves with a pool of dead air down the middle of the left one. Everything from the rule down is a multiple of the list's pitch off the list's first row now, and both columns rule at the same horizon. Under the code that rule also reads as the field's underline, which the field never had.
  • The record well on the front page keeps one inset. Its two columns were inset by a literal number in the draw code and by a differently named literal in the test that proves the well is wide enough for its longest row -- two spellings of one number, with the test measuring the one that does not print. The brackets around the rows went at the same time: the fill and the heading carry the well on their own, and the frame lines were drawing a box around a box.

Where the shell stands

  • A scene can carry weather. The backdrop format gains an ambient block and six layers that read it: rain, ash, mist, a flicker in the lamps, a storm over the whole frame, and a shimmer through the water the scene stands in. Every one is optional and every one is declared by the scene that wants it, so a scene that says nothing pays nothing.

One depth per particle. Every streak, mote and haze bank rolls a single number, and that number sets its fall speed, its size and how far it swings when the frame is steered -- in the same units as the scene's own layers, so the weather is inside the picture rather than on the glass in front of it. Rain straddles the front layer deliberately: at one depth a streak falls behind the railing and at another it falls in front of it.

The pass is split in two, and the split is not arbitrary. The lamp glow and the shimmer are in the scene -- the lamps are painted into the art and the reflections are the art -- so they go on under the screen's veil with the pixels they belong to. The rain, ash, mist and lightning are in the air between the camera and the scene, so they go on after it. Veiled weather reads as weather behind glass.

  • Two new places, and the shell moves house. The library is sixteen backdrops now: a dry dock -- a ship still in a repair basin whose gates have failed, four lamps along the far wall, one work lamp off the gantry and a hundred rows of standing water under all of it -- and a train shed, which is the roofed view of the same flooded platform the station looks at from outside.

ARENA stands in the dry dock, the JUKEBOX in the train shed, the CREDITS at the flooded station and the OPTIONS tree in the cable car. The screens between ARENA and SURVIVAL name no scene of their own any more and inherit the one above them, so walking down through mode, map and bots holds one place instead of cutting to a new one at every step. The station and the dry dock are the two scenes carrying weather today.

  • A chroma keyer, because the plates changed. The image model has stopped returning transparency and started returning a flat magenta plate, and the flood fill that cut the older renders is the wrong rule for that in both directions at once: the dry dock's water runs to the bottom border, so a border seed grows straight into it and the flood takes the water away, while the magenta trapped inside the gantry's lattice touches no border and survives as pink triangles.

The new rule measures a pixel along the axis the plate stands out on rather than as a distance in colour space, because an edge pixel is a blend of plate and art and a blend moves along that axis and no other. The alpha is how much art is left, and the colour is that art recovered by taking the plate's share back out -- a pixel left at full alpha with the spill still in it is the pink rim this exists to stop. It cannot fire on a scene the old rules already handle: measured across all 62 source renders in the library it fires on the dry dock's three cutouts and leaves the other 59 alone.

The credits

  • The roll climbs out, and ends on the wordmark. It used to park its last line in the middle of the screen and hold it there for the minute and a half the song has left, which is a long time to look at a frozen list. Now that the credits stand in front of a moving scene it carries the last line clear of the top edge instead, and the frame it leaves empty is taken by the wordmark, fading up over a second and holding until the song ends or you press anything. A track shorter than the roll is noticed now rather than being left playing to nobody.
  • The player takes the platform when the credits end. His own character walks in from off the left along the flooded foreground, submerged to the chest and rippling where the shimmer crosses him, steps up onto the platform as he reaches the lamp, and stays -- standing, looking around, and taking the occasional short walk to break the loop.

He is behind the foreground rather than in front of it, and that is the point: the near layer at this end of the platform is the flood and a parked flatbed cart, so it closes over him at the chest wading in and crosses him at the waist standing under the lamp. The occlusion is the depth. In front of it he was a sticker on the picture.

The behaviour is a state machine rather than a function of the clock. The pacing walker on the front page is the same clock every time, which is what makes a metronome of it; this one rolls for how long it stands, which way it looks and whether it walks at all. His legs are driven by the ground he has covered rather than by a frame rate, so the feet cannot outrun the floor at any speed -- the first version paired a stride written down twice with a cadence in frames per second, was out by a factor of three, and shuffled on the spot.

Tools

  • A parallax editor. A sixth browser tool, at /tools/parallax-editor.html, for the backdrops the menu stands in front of. Its layers are dragged over the renderer the shell actually draws them with, and the two rules a scene has to keep -- a plate wide enough to cover the frame at both extremes of its drift, and blur that never increases toward the back -- are reported while a number moves rather than by a test long after. Sweep runs the aim to both limits, which is the only way to see a plate that is too narrow.

The menu's own furniture draws over the art, read from the constants menu.js exports rather than copied, so it follows when a menu row moves: the scrim, the rows, the value column, and the box the forest's cabin aims its lit window into. A character can be stood or set pacing anywhere in the scene, and standing is a pose the format states now rather than one it fell into by accident. Erase and pick repair what a chroma key left behind on a plate. New starts a scene from loose plates. Export writes an <id>.asset that npm run asset -- import installs, and saving splices the values that changed into the lines the old ones occupied, so every measured comment in a scene.yml survives being edited by a tool.


0.9

the shell becomes a place, the loadout becomes a choice, and it rains.

0.8 filled the arena. 0.9 is about everything around the fight. The menu stops being a list on black and becomes a street inside the cordon, with your own soldier pacing under the lamp and a personnel file that says who you are; the loadout stops being a colour and becomes three perks off a pool of twenty five, each one trading something away for what it gives. An arena can be under weather. There are thirty one of them now rather than sixteen, every one of them with ground underfoot and a sky overhead, and the walls they are built from can carry a decal, cycle their own palette, and stop shimmering at a distance. Everything a tool makes leaves it as one file and installs itself with one command. And a body no longer lies where it fell forever: it comes down into ash and blows away.

Perks

  • A loadout is a choice now. Three slots on the CHARACTER screen, twenty five perks to fill them from, and every perk trades: LIGHTFOOT is twelve percent on the walk and the run against ten points of health, SIDEWINDER buys back the strafe-at-a-run the movement rules deliberately withhold and charges the forward run for it, GLASS CANNON hits harder and is hit harder. Nothing in the pool is free, and the test suite refuses a perk that declares no upside or no downside, so the balance model is a property of the data rather than a convention somebody remembers.

A perk is data and carries no code. It names signed deltas against a namespace of tunable keys -- walk speed, magazine size, reload time, revive hold, hearing range, minimap visibility, the lot -- and the three you equip resolve into one frozen bundle when the match starts. Multipliers fold as a product, additions as a sum, booleans by OR, and every key is clamped, so three stacked perks cannot reach an absurd number and a multiplier can never invert a sign.

What reads the bundle is everything: movement, the reload and the magazine, damage dealt and damage taken, throws, sentries and crates, the revive hold, how far you hear, what the minimap shows about you and what it shows you. The perks live in game-data/config/perks.yml, so the pool and its numbers are an edit to one file.

  • The file says what the loadout comes to. A NET EFFECT ledger on the CHARACTER screen adds the three equipped perks up and prints the result -- cancellations and clamps included, so two perks that pull opposite ways on the same key show as the one number they actually produce rather than as two claims you have to reconcile. PERK SELECT opens the whole pool with each perk's gain and cost beside it, and previews the loadout equipping it would give you before you commit to it.

The menu

  • The front door looks out at the district. The root screen stops being a list on black: behind it are the four parallax layers of a street inside the cordon, drifting on their own sines, with your own character on the lit platform under the lamp, in the colours you picked, pacing and turning. Down the right is the career you have accumulated. The layout is the map screen's language brought to the front -- header band, footer band, a left rail, a framed well -- because that screen had already answered how type and a picture share 480 by 300.

The front layer carries a defocus, so the fence, the barrier and the drum sit in front of the picture rather than in it, and the walker and everything behind him stay sharp. It is a runtime blur paid for once when the layer is decoded, and its radius is authored in the scene's own file rather than in code.

The record well down the right gave the middle back. It was 191 pixels of mostly empty plate sitting over the lit shopfront and the billboard, which is the part of the backdrop worth looking at; it is 95 now, with its right edge exactly where it was.

  • Fourteen places for the shell to stand in front of. A street, a forest, a bar, a beach, a cable car, a car park, a cinema, a hospital, a mall, the Parthenon, a petrol station, a station, a supermarket and a warehouse. Which screen stands in front of which is data now -- game-data/art/parallax/scenes.yml -- rather than a name written into the shell, so moving a screen to a different place is an edit to one line. A scene needs neither a walker nor a ground to be usable, which is what lets the centred screens have somewhere to stand too.
  • The OPTIONS tree steps out into the forest. The five options screens share their own chrome and their own rail rather than the generic list draw, over a backdrop of their own, and the controls legend comes up out of the footer where it used to be filed.
  • The menu answers a mouse. Every list in the shell moves its selection to the row under the cursor and opens the row you click: the front page, the room code and mode pickers, the arena rail, the bot and run length screens, and every options screen. Right click is BACK anywhere in the shell, including the screens that have no rows to click at all, and the wheel scrolls the arena rail a row at a time. A live match keeps the right button: a click during a fight belongs to the fight, not to a menu.
  • The rows that adjust with left and right say so. GRAPHICS, GAMEPLAY, CONTROLS, BOTS and RUN LENGTH draw a < and a > on the selected row, the same rule the caret already follows, and both are pressable. Before this there was nothing on screen to aim at and nothing to discover: the keys worked, and you had to already know they did.
  • The volume bars are sliders. Click anywhere along MUSIC, SFX or VOICE to set that level, or hold and drag it. The bar was already drawn as a slider; it simply did not behave as one, and walking to a value the cursor is pointing at in tenths could not have reached most of the values the bar can show anyway.
  • Nothing about the keyboard changed. The mouse is a second way in, not a replacement: every arrow, Enter and Escape does exactly what it did, and the pointer works by asking for the same keystroke a player would have pressed.
  • A touch pad button no longer confirms the row underneath it. The on-screen pad covers the whole viewport while the canvas is letterboxed inside it, so on a wide enough phone the UP button sits directly on top of OPTIONS, JUKEBOX and CREDITS -- and tapping UP opened whichever one it was covering. Both pointer listeners now ignore anything that is not the canvas itself.

The character screen

  • The CHARACTER screen is a personnel file. It was three rows on a flat field with a second screen behind COLORS; it is one page now -- the figure as the photograph, the subject, the posting, the faction, the loadout and its ledger, all visible at once. The COLORS subscreen is gone with the two-screen model, so Escape has one meaning again.

The photograph is 160 by 224 and the figure in it is at a whole 4x, the close-up at a whole 5x. Every view of the figure is blitted at a whole scale and none of them takes a scale as an argument: each is handed a rectangle and works out the largest that fits, so a sprite that used to be the one resampled thing in the game cannot become one again. The close-up tilts toward the part of the figure under the cursor, aimed off mask.png, because nothing else in the data knows that a hazmat visor is a band across the face while a hero's skin is a face plus two forearms.

SUBJECT names the role -- HERO, POLICE OFFICER, CIVILIAN, HAZMAT -- rather than the person. The file is about the name you type in, and printing DANNY VOSS one row under SOLDIER gave one figure two names, the bigger of which was not the one it answers to. The lore is not deleted, only unread here: it is still in each character's yml, and the story page and the character editor go on reading it.

Weather

  • An arena can be under weather. Rain falls over the frame as streaks that are depth tested against the world, so a near wall stands in front of them and a far one does not, and it splashes where it lands -- each splash placed by asking the depth buffer for a floor pixel within four and a half cells of the eye, so the ground reacts to the rain instead of the rain merely passing over it. Lightning washes the frame, and thunder follows just under two seconds behind, far enough back to read as distance rather than as a sound effect attached to a light.

Three presets ship. clear is nothing at all, and costs one boolean read a frame. drizzle is wet without being weather: the streaks and the sound and none of the drama. storm is the front actually overhead, firing a strike every six to eighteen seconds -- rolled inside that range rather than counted off a metronome, because a strike that lands on a beat stops reading as weather.

Rain belongs to the outdoors, not to the cell you happen to be standing in. Every streak has a place in the arena, so what decides whether it falls is the sky over the point it is falling through rather than the sky over you: stand in a doorway and the street ahead is full of rain while the room behind you stays dry, and stepping out has nothing to switch on. A splash is settled once by the ground it lands on, so a ring out on wet tarmac keeps rippling while you watch it from inside. An arena with wind rains at a slant, along the same two map keys the ash already falls on, and it rains just as hard: the slant widens the band the streaks are placed in and the count widens with it, so turning on the spot does not thin the rain out.

The rain bed cannot be placed that way -- it is one voice standing in for every drop in the arena at once -- so it changes character instead. Indoors it drops to about a third and loses its top end, which is what a wall does to rain, and it is the same voice throughout: crossing a threshold no longer stops the loop and starts it again from the head of the clip on the way back.

A strike lights what the frame can see of the sky. The wash is full out in the open, weaker through a doorway, and nothing at all in a windowless room, so a basement no longer lights up as brightly as the road outside it. Thunder is never cut, because thunder carries through a wall: from inside it rolls at about half the level and with the crack taken off it, which is what still tells you a storm is on once the flash has been scaled away.

A map declares it with one weather: key, and the map editor picks it from a dropdown beside the sky. The developer console has a weather verb -- weather storm, weather rain 0 through 10, weather lightning, weather thunder -- and it drives your own client and nobody else's, because waiting on a round trip to see a flash would make it useless as a tool.

  • Everyone in the room sees the same strike. The referee is the only client that schedules one: it rolls the gap, decides the moment, and puts it on the wire along with the seed the flicker pattern is derived from and the name of the thunder clip it picked. Two people standing together under one sky therefore see one flash flicker the same way and hear the same roll, rather than merely flashing at the same time. A strike a map's own rules fire agrees the same way: the referee rolls one seed for the firing and sends it beside the rule, so a set-piece strike is one strike everywhere rather than a different flicker on every screen. The rain level and the preset are not moments but state, so they ride the referee's snapshot and a joiner arrives into the weather already falling, the same way it learns which doors are open.

The protocol moves to 33. A client on 32 lands in a different room rather than in one where half the arena is raining -- so this build does not connect to the one before it.

  • Three things this does not do yet, said here rather than left to be found.

A strike washes the screen and does not light the walls. Lifting the arena's own ambient for the frames of a flash means either rebuilding the flat-fill shade table every frame or re-baking the lightmap every frame, and those two paths are required to produce identical pixels at a uniform ambient, so lifting one and not the other would break that promise. The wash covers the whole frame anyway, the weapon in your hands included. It is a deliberate fallback rather than an oversight, and the walls flaring is the part that was given up for it.

No shipped arena is under weather. The library, the runtime and the editor row are all here; which arenas should be rained on is a decision about each map rather than about this, and none of those has been made. Until one is, the editor and the console verb are how you see it.

The four clips -- the rain bed and three thunder rolls -- are the repo's own generated placeholder tones rather than recordings. Each carries a brief in PENDING_AUDIO_ASSETS.md saying what a real one should sound like.

Arenas

  • Fifteen arenas that quote a place you already know. DUST, TWOFORT, FACING, PADDOCK, HELMSDEEP, GLASSBRIDGE, NAKATOMI, ARENA, HEDGEROW, GULCH, VILLAGE, MORIA, NUKAGE, HIVE and UPSIDE DOWN -- long A and short B, two forts and a bridge, one wall between you and the horde, a glass tower and no shoes, the mall but wrong. Seven new wall materials were authored to stand them on.
  • PARTHENON, a ruined temple on a bare hilltop. A peristyle five storeys tall around a cella, built at the real building's own proportions and seen from the far end of itself, on a crag with nothing else on it. Two facade kits came out of it -- a fluted Doric shaft with its capital, and the crag the temple stands on.
  • NORMANDY, the landing. 48 by 60, the largest single-floor grid the game ships, and 22 of those rows are open sand, because the emptiness of the killing ground is the beat that matters. It is the arena the open horizon and the beach materials were both built for.
  • The world does not stop at the border. A map can name the material cast as ground past its own edge, so an arena can look out at a sea rather than at its own sky mirrored and darkened, and it can say how far below its floor that plane lies. A sky can carry the water it reaches at its horizon. And openair is a border that draws nothing at all, so the ground can simply run out of the world with no visible edge.

It is cheaper than the mirror it replaces -- 2.84ms off the wall pass at the deepest band a body can stand for -- because the mirror sampled a 512 by 256 sky face where the cast samples a resident 64 by 64 tile.

  • The beach is made of things. Six materials, three obstacles and a sky, so a landing can be authored rather than approximated. The sea is the point: every wall generator that can cycle its palette is directional by construction, which is why the stand-in showed chevrons looking west and a still lake looking north. swell is a summed sine field with equal frequencies -- isotropic and tiling by construction -- and it animates by frames, so the world past the border learned to resolve its texture per frame.
  • Every playable arena has ground underfoot and a sky overhead. Seven of them were painting their floor, their ceiling or both as the flat shade-table wash; all seven are dressed now, and three wall materials were authored for the job. PANELS and SCREENS, the two arenas that existed only to be looked at, are gone from the list. Fourteen headless drivers stood on PANELS as their fixture and stand on BAZAAR now.
  • PLAZA is four floors and a lift to each. The arena becomes four regions of one 24 by 77 grid -- roof, offices, plaza, undercroft -- linked by all four portal pairs as lift mouths. The square is paved and ringed by four raised facades and lit by its own poles under a moon sky; the car park is dark between its lamps; the offices are a tight corridor ring; the roof looks out over a raised skyline. A portal whose kit declares a ride plays its own car clip, once, at the mouth it delivers you to. Survival marks eight wave points on every floor.
  • The square and the yard become streets. PLAZA's ground reads as a city street -- a ring road around a cobble island, paving slabs in the four arms, and a painted crossing out of each of the three lift mouths, taking over the wayfinding the hazard stripes carried. YARD gets the same treatment with the six urban materials PLAZA landed: a cell of pavement round every block, carriageway everywhere else, lane lines down the centre of each avenue and a crossing on every door that faces a road. No cell of either grid moved.

YARD's ground is a rule rather than a shape, so the rule lives in a script, the rectangles it prints live in the map, and a test re-derives the rule against the parsed arena: move a wall and it names the cell whose pavement did not follow.

  • Twenty more storeys for the skyline. Twenty opaque wall materials for the upper floors of a raised block, one storey per tile, each with a facade kit of its own so they reach the height layer. The wall library goes from 88 materials to 140 across this release.

Decals

  • A decal is art laid over a tile rather than art replacing it -- an arrow on brick, a beacon on concrete, a crate marker on plaster, with the wall showing through around the mark. wayfind, beacon and cratemark leave the wall registry and come back as a decal group of their own, joined by a neon shop sign for the upper storeys of a raised block.

A map places them through a decals: block, one line per surface, stacked in file order, with a flip and an axis-aligned rotation per placement. Each decorated surface composes its stack onto the tile once at load, so the renderer samples a decal exactly as it samples any other texture and pays nothing per frame. A decal composes onto floor and ceiling cells as well as wall faces, cycles if what is under it cycles, and emits light -- spread into the cells around it the way a wall emitter's light is, because a light at the centre of a solid cell contributes nothing. A material a bake cannot serve falls back to a per-texel overlay, and a decal on a surface with no material at all is skipped.

Baked rotation is what retires wayfindback, the pre-rotated material that existed only because the floorcast samples world x,y straight into texel x,y and can never turn a tile. A baked texture arrives already turned.

  • The map editor places them. A decal can be placed, stacked, reordered, flipped and rotated from the cell menu, and a decorated cell is badged on the edges that carry one.

Walls

  • A wall material can cycle its own palette. A material's yml can declare cycle: bands, each band a rate and an ordered list of colours that already exist somewhere on the material's own texture; the game advances each band on the shared art clock at its own rate, rotating which of those positions each colour sits at, so the surface moves with no second frame of art anywhere and no sprite sheet to cut. The decode of the material's PNG IS the live texture buffer -- nothing is copied or repainted -- and only the texels a band actually owns move on any given tick, so the cost stays tied to how much of the tile is cycling rather than to the tile's size. HOLLOW's rock face carries the first one: water runs down it, drawn from eight colours in a strip nobody re-renders a frame of.

Nothing about a cycling phase crosses the wire or touches simulation. Every player advances the same band off their own clock, so a peer seen mid-frame is not out of sync -- it is looking at a different moment of the same water.

The wall editor authors a band the way it authors any other layer: paint a ramp, mark the run as a band and set its rate, and the preview animates it live at that rate, in the same panel that already turns a recipe into an exported material.

  • Eight cycling fixtures join the library, and none of them is a stripe. A radar scope sweeps, a fan turns behind its hub, chevrons walk along a wall toward the exit, a beacon alternates two lenses, a crate marker pulses in the same teal the minimap paints an item, rain runs down a pane you can see through, and a scope trace travels along its own waveform. Every one is a single 64x64 PNG that never animates: what moves is which colour sits at each position, exactly as the water on HOLLOW's rock face does.

Three new layer kinds in the wall editor put those positions on a tile. sector assigns them by angle, so rotating the ramp sweeps a wedge around a circle; streak scatters sparse runs each carrying the ramp end to end, so a bright head travels along every one at once; marks lays out a row, column or ring of lamps, bars or chevrons and gives each the next position, so the lit one walks the arrangement. Unlike a band, none of the three covers the whole tile, which is what lets a fan keep a static hub, a beacon keep its housing, and a tile carry two cycling layers at different rates for the first time.

A generator now says for itself whether it cycles, rather than the exporter keeping a list of the kinds that do, so a ninth kind needs no edit outside its own file. And the shipped materials are gated: every committed tile is re-rendered from its own recipe on every test run and compared texel for texel, which catches a palette colour edited without a re-render before it becomes a boot error naming a band position and nothing else.

Weapons

  • Thrown weapons, rockets and enemy fire cross a see-through portal without a jump. An opaque portal stops them like the wall it looks like.
  • Walking over a better weapon can raise it. OPTIONS > GAMEPLAY > AUTO SWITCH is off by default, so nothing changes until you ask for it; on, a weapon you pick up that outranks the one in your hands is drawn for you. Which weapon outranks which is authored per weapon, and the three throwables -- the grenade, the sentry and the drone -- are ranked out of it entirely: they are taken like any other pickup and never put in your hands.

The swap waits for your hands. A pickup taken mid-burst, mid-reload or down the sights is remembered and raised once the shot has played out, so the setting never takes a gun away from you in the middle of using it, and reaching for the digit row cancels a pending swap outright.

  • A reload puts the sights down. Holding the aim button through a magazine change used to leave a scoped rifle magnified at the one moment both hands are on the gun; the pose falls the instant the reload starts and takes the scope, the magnification and the aiming penalties with it.

It is suspended rather than cancelled. An aim you never let go of comes back up on its own the moment the magazine is in, and an aim pressed during a reload waits its turn: it raises nothing while the reload runs, and it does not abandon the reload the way a trigger pull does. The four weapons with both sights and a magazine -- the pistol, the shotgun, the chaingun and the rifle -- all read the one rule.

  • A throwable's ammo box hands over the weapon as well. Walking onto grenades, sentries, drones or axes arms you with the thing that throws them, so a reserve you were already carrying and could not use is a weapon you can. It is the four throwables and only them: an ammo box for a gun is rounds and nothing else, and a shotgun still comes out of a crate or off a body.

A full reserve no longer means walking past. A box you have no room for is taken anyway when the weapon in it is new to you, the same courtesy a floor gun already extended. Nothing is put in your hands either way -- the throwables sit outside the autoswitch ladder, so the box arms you and leaves the weapon you were holding exactly where it was. Bots pick a box up under the same rule.

  • A weapon on the floor is the weapon. Six of the eleven -- the shotgun, the pistol, the chaingun, the rifle, the flamethrower and the missile launcher -- ship a drawn floor pickup cut from a side-elevation render rather than a sprite baked from the HUD icon, and the axe, the drone and the sentry are cut from the sheets they already ship. The two routes stand in for each other one weapon at a time, so the remaining five are unaffected. Every weapon declares its own drop.png, which means overriding one is dropping a 64 by 64 file into place.
  • A deployable is picked up at the size it is thrown at. A sentry or a drone on the floor no longer changes scale between leaving your hand and being taken back off it.
  • Gun game's ladder names weapons rather than counting them. It was a list of indices into the authoring order of weapons.yml, which made an art manifest's ordering load-bearing for a rule in another file; it is weapon ids in game.yml now, validated at load against the roster, and each rung's reserve is read off its own weapon.

Matches

  • The knife takes a rung back. Gun game issued exactly one weapon, the rung you had climbed to, so the knife at the foot of the ladder was a weapon you met once, on the last rung, one frag from winning. It rides every rung now: a gun game loadout hands you the rung weapon and the knife together, the rung weapon in your hands and the knife one press of 1 away.

What that buys is a way to lose ground. A frag dealt with the knife promotes the killer the way any frag does and DEMOTES its victim one rung, down to the bottom of the ladder and no further. Killing the leader with the weapon nobody has to earn is worth two rungs of the gap between you.

Your rung is still your frag count and nothing else, so a demotion is a frag taken back: the RUNG and LEAD cells, the weapon you come up holding, the rank and the frag column all follow one number, and a demoted player's board reads the rung they are actually on. Which weapon does it is the ladder's business rather than a rule about the knife -- it is the bottom rung, whatever that is authored to be, so the weapon you can always fall back to and the weapon that costs its victim a rung cannot drift apart.

  • LAST MAN STANDING enforces one life again when you play it against bots. A lone player who picked it and set a squad of opponents got an arena that looked completely normal and followed none of its own rules: everybody who died came straight back, no round ever ended, and the banner read WAITING FOR PLAYERS the whole time. The mode needs two combatants to start, and bots were not counted as combatants -- so the match sat in warmup forever, and warmup is the one phase in which a one-life mode does let you respawn.

A bot is a combatant now. It fills a slot the mode is waiting on, it holds a place in the standing count the round is decided by, and it can be the last one standing and take the round. What it still does not do is count as a person: the squad size is worked out from the humans in the room, so a bot never makes way for itself.

  • The arena says its name. A match used to open on a picture with nothing in it to tell you where you were: the map was named on the screen you picked it from and then never again, so a player who joined an invite arrived in a place they could not have named. It writes itself across the middle of the view now for about three and a half seconds when a match starts -- a plate opens, a wipe drags the name out of it left to right, the arena's one-line blurb arrives underneath, and the whole thing collapses back to a single amber rule and goes.

It sits in the band at y 84 to 146, which is the only strip of the view that was free. The phase banner owns the top of the screen and the music widget the top right; the crosshair sits at y 150 and the gun below it. A card an inch lower would cover the reticle at the exact moment a fight starts.

The wipe rides the same cubic ease the screen transitions run on rather than a curve of its own, and reduced motion gets the whole card at once with no ramp either side, the way every other fading overlay already behaves. An arena with a name too long to fit steps the type down a size rather than running off the edge, so an authored map cannot break the card by being called something long.

  • How far up you can look is yours to set. The camera has always been pinned to 14 degrees off level, up and down alike, which is a fine cone to fight in on flat ground and not enough to see what is standing on the floor above you. OPTIONS > GAMEPLAY carries a LOOK LIMIT row now, stepping from that stock 14 up to 26 in threes, and the setting applies the moment you close the menu rather than at the next match.

26 is the ceiling, and it is a real one rather than a shrug at a round number. The renderer sizes the tables its floor is cast from off that limit, and the same clamp is what bounds how steeply anything can be thrown -- at 26 degrees the steepest aim is still a throw and not a mortar. The ladder itself is data, in game-data/config/game.yml, so the rungs are a config change and not a code one.

  • A bot can win a match. A bot's frags always counted on the scoreboard, but the crown itself was gated on the combatant roster, and that roster leaves bots out by design, so a bot standing at the frag limit was treated exactly like a player who had disconnected: passed over, every frame, forever. In a room with people in it that meant a bot's lead simply did not end anything, and in a room of nothing but bots -- which is what the always-on arena the front page watches is -- nothing could end the match at all. Those rooms fought on past the limit with frag counts in the hundreds, and no results board, no winner and no rematch ever came.

The gate stays where it is for players, because it is there for a real failure: a peer the relay drops without ever announcing it must not win on a row nobody is behind any more. A bot has no such failure to guard against. It is owned by the machine running it and mirrored from there, so it exists exactly as long as its owner says it does.

  • A room of bots is a match, so its countdown runs. A room with nobody human in it never left warmup, which meant the always-on arena stood in a lobby forever. The warmup banner asks the phase machine what phase it is in rather than counting heads for itself, so the two can no longer disagree.

What is left of a body

  • A dead body drifts into ash. A corpse's own texels come apart and settle into a drift on the floor -- an enemy's, a bot's or a player's, the same rule for all three -- rather than a body lying where it fell until the round ends. A killing blow shoves the ash away from itself, so the drift remembers which direction the shot came from, and a body killed by fire makes charred ash.

An arena has a wind direction and a map can set its own, so the drift travels rather than piling. A grain never overtakes the one it landed on, which is what stops a drift reading as noise, and the whole drift paints itself into one sprite once it has settled rather than staying a cloud of points. The pool caps and evicts, so an arena full of bodies costs a bounded amount.

  • Blood is a wound, not a splash the size of the body. The effect was scaled off the figure it came from, which made a hit read as an explosion at close range; it is sized to the wound now, and big enough to be seen from the range it was made at.
  • Bullets scuff the ground, and water splashes. A hitscan shot could not hit the ground at all -- shots resolved against walls and bodies only, so aiming down at the sand and firing drew nothing. The floor crossing is solved directly from the aim pitch, and the mark is presentation only: the shot still does whatever damage it was going to do. A grenade landing on sand kicks up sand, from authored art with structure in it rather than a blob of soft discs, and a ground effect rests on the ground rather than floating over it.

Characters

  • Characters have names. HERO, POLICE OFFICER, CIVILIAN and HAZMAT are the jobs they do; DANNY VOSS, SGT. MARCO REYES, CLAIRE ADDISON and DR. ANTON NORDLUND are the people doing them, written into each character's yml along with where they came from and why they are still in the district. Your own file does not call them by those names: SUBJECT prints the role, because the only name that screen is about is the one you type into it.
  • A voice belongs to a character. It used to be a sixth thing you picked, a VOICE row offering five personalities that any character could wear, so nothing stopped a hazmat consultant sounding like a drill sergeant. The row is gone: choosing a character is the whole of choosing how you sound, and Enter on the CHARACTER row auditions the one you are looking at. Bots speak with the voice of the character their slot already gave them.

The clips moved with the decision. Each character's 21 readings live in its own directory beside its sheet and its mask, and the lines and the casting brief they were rendered from are a voice: block in its character.yml, so everything one character is made of is in one place. WRENCH had no character to belong to and is gone with the bank it came from.

Nothing about this crosses the wire as a new field. An appearance no longer carries a personality at all; the clip is composed from the character every hello has always sent.

  • POLICE OFFICER and HAZMAT are men. The district has one woman in it, CLAIRE ADDISON, and the back stories say so.
  • The district has a bible. The Yard, its factions, what happened to it and the form nobody can find are written down as a document rather than implied by four character files, and the twenty survival creatures get an account of where they came from.

Bots and enemies

  • Bots aim like something that has to find you. A shared aim model gives bots, survival enemies and turrets a facing that swings and settles, a belief about where you are that trails a moving target, and a trigger that holds until the barrel is near you. The random offset that used to be the whole of a bot's inaccuracy is a residue of that now rather than the mechanism. Spectating a bot reads as a turn rather than a yank, because the camera takes the bot's aim vector as its look direction and the aim vector is no longer a snap.
  • Bots and enemies ride lifts. The steer aimed at the far approach cell rather than at the portal mouth, so an AI body walked past the thing that would have carried it to the floor it was trying to reach. PLAZA's four floors are reachable by everything in the arena now, not only by a human.
  • A bot's body stays out of walls. The bot stepper tested one point on the leading edge of the moving axis, so a bot running alongside a wall never looked sideways at it and let its body box overlap by up to a whole radius -- which is what a player sees as a body standing half inside brick. It makes the same box check the player's own mover makes, so the two cannot disagree.
  • The pathfinder sees solid cover. A route is planned on the graph a body actually moves through, so a lightpole stops being part of a plan, and a route can be told to prefer not to enter a cell without being forbidden it -- a preference rather than a wall, so a bot with no way round still crosses.

Props and portals

  • A prop collides with the shape it draws. A prop's silhouette becomes a half-width per height rather than one box, so a projectile hits the art instead of the bounding volume, and a thrown axe stops sticking in mid air next to a lamp post. A raised prop is not solid down to the floor.
  • An open portal is crossed without a lurch. A see-through pair moved the view 0.47 cells down the view axis in one frame: the far side is drawn face centre to face centre, but the body fired the crossing on its leading edge, a radius before its centre reached the face, and landed a clearance plus a radius past the mate's. An open mouth now stops blocking a body whose centre is on its outward side, the crossing fires on the centre, and the exit is the seam image. Measured in the shipped build: 0.470 cells before, 0.050 after, which is one walking frame. Closed pairs are untouched -- still leading-edge, still pushed clear, still faded.
  • Everything in flight crosses an open portal. Grenades, thrown blades, rockets and enemy bolts all take the same crossing rule the body takes, and bounce off a closed one like the wall it looks like.

What the frame is made of

  • A repeated tile stops beating against the pixel grid. Every loaded texture carries a chain of box-filtered levels, and a wall column, a floor row, a cycling material, a baked decal and a repainted display each sample the level their own sampling rate asks for, dithered so the change of level is not a visible seam. A tiled wall seen at an angle, or a floor running away from you, no longer crawls.
  • Texture filtering is a graphics option rather than a console verb. OPTIONS > GRAPHICS carries a FILTERING row stepping through off, soft, normal and sharp, and the gate behind it measures high-frequency energy in the frame rather than trusting the setting to have done something.
  • A building stands over a doorway. A raised block's storeys stopped dead at a door cell, leaving a hole in the facade where the lintel should be; the march records the raised cell at the door's own face and the wall pass paints that stack over the finished column, so the building continues over the opening -- through a portal too, and a nearer lintel occludes a farther one properly.
  • A storey stack is pitched on the true unit. Floored, the column unit's rounding error was multiplied by the storey index, so a high storey's top edge stepped in blocks of rows rather than one row at a time. Splitting the floored unit -- storey zero, the floor span -- from the true storey-to-storey pitch holds the drift under a row at any height.
  • The floorcast skips the mip machinery at level zero, and the performance panel splits OTHER into the time the browser was holding the frame and the time the update actually took, which was one bar hiding two very different answers.

Sound

  • Sixteen placeholder tones become recordings, the five drone clips among them, and a lift sounds like a lift rather than like a door. All but a handful of the game's gameplay clips are real sounds now; every one that is still a generated tone carries a brief in PENDING_AUDIO_ASSETS.md saying what a real one should sound like.
  • Two more tracks in the JUKEBOX. SHADOW PROTOCOL and RESTRAINED HORIZON are four minutes of synthwave each, cut from lossless masters, and they sit with the other retrowave tracks. Neither is wired to a map or a menu, so nothing that played before plays differently now.

The tools

  • One file leaves an editor, and one command installs it. Every editor used to end in its own copy of a download call and leave an author dragging files out of a downloads folder; the map editor could not export at all, and an arena reached the repo only by copying serialized text out of a developer console. A tool now exports one .asset -- a store-only zip carrying a manifest and a payload -- and npm run asset -- import places the files, joins the registry, records a ledger, and refuses when the tree has moved under it.

All five families are on it: props, wall materials, characters, weapons and maps. The character editor is the one this was for -- it fired one download per file, and a character with a full voice bank is 24 of them against a browser cap of about ten, so it could not hand over a complete character at all.

The manifest says what a subject is and never where it goes, because a wall material's tile index, and the counts four tests pin on it, are answers about the repo on the day of the import rather than the day of the export. A material's import performs five registrations, three of which edit test files, and every one of them is computed against the tree at import time.

masters/ is retired on the way: a master sits beside the character or the creature it describes, under game-data/art/, which is the only place a browser tool can fetch one.

  • An exported arena takes its art with it. Export in the map editor used to write the map file alone, so a map built on anything the recipient did not already have arrived and did not load: a prop they lacked, a material outside their registry, a shop kit behind its windows. An .asset now carries the map plus one complete nested asset per subject the map names, walked transitively, so a facade kit brings its own materials and the rooms behind its glass. The export dialog says how many went in and how large the file came out, and offers no way to leave one behind, because a map with a dependency deselected is a map that does not install.

Installing one is the same command it always was, and it does the least it can. A dependency the tree already has byte for byte is left exactly where it is -- the ordinary case, since anyone with the base game has most of what any arena names -- and asset remove map <id> takes back only what the install itself wrote, sparing every file that was already there. A dependency present with DIFFERENT bytes is a real disagreement about what a prop or a material is, and it refuses the whole import by name; --force does not lift that one, because overwriting a prop's sheet changes every arena in the tree that stands one.

Two things an arena deliberately leaves behind. Its track: names an entry in a game-wide music library, and a single ogg runs to several megabytes. Its crate tables name weapons, and following one reaches the whole weapon library. What travels is what an author placed in the arena.

  • Play opens an overlay, and the arena picks up edits live. The map editor's Play button no longer navigates the tab away: it opens the game in an overlay you can shrink into a corner, and every edit reaches the running arena on a short debounce -- no reload, no restart, and you keep the spot you were standing on.
  • The map editor can say where in its cell a door hangs. A door slab sits at the middle of its cell, at the near edge or at the far one, and which it is has ridden in the map file since door types landed -- but the only way to choose was an R keypress on the brush that nothing on screen mentioned, and that keypress could only set up the NEXT door painted. A door already on the map could not be changed at all short of painting over it.

Both halves are on screen now. Selecting a door brush raises a DEPTH row in the rail, three buttons wide, showing which depth the brush is loaded with; and right-clicking a door already placed opens a DEPTH section that changes that one door where it stands, keeping its type, in a single undo step. R still cycles the brush for anyone who learned it.

The menu names the edge rather than the vocabulary. near and far are map coordinates -- near is the lower one -- so a row reading Near tells a mapper nothing about which side of the cell they are choosing. The menu knows which wall the door sits in, so it offers Flush west, Centred and Flush east for a door in a north-south wall, and Flush north and Flush south for one in an east-west wall. The rail's buttons keep the bare words, because a brush has no cell yet and so no edge to name.

  • Every door on the canvas shows where its slab hangs, as a dotted line across its cell. A centred door used to draw nothing at all, which left it looking exactly like a door nobody had thought about -- the one pair worth telling apart. The line is dashed rather than solid, and in a colour of its own: the mark it replaces was filled with the canvas's own background, and read as a gap in the door's art rather than as a note about it. A door with no wall to sit in draws its line red, the way an ambiguous portal facing already does.
  • A character's text lives where the game reads it. The back story and the voice were written in a master file and copied into the character file the game loads, with a test existing purely to notice when the two stopped agreeing. The text lives once now, and the character editor grew a fourth mode beside mask, sheet and head that holds the name, the lore strings, the back story, the casting brief and all 21 readings -- each one playable, and replaceable with an .ogg imported from disk. A field that would not survive the round trip is marked where it is typed and Export refuses while it stands, so a curly quote is caught on screen rather than an hour later.
  • A character starts on a base, not on nothing. New in the character editor offers three base figures to build from, each one drawn to sit inside its own cell.
  • The weapon editor shows the floor pickup and takes a replacement. Every weapon's drop.png is visible in the editor, held to the same rules the shipped art is held to, and carried out in the weapon's asset.

The site

  • The release notes get a rail. Every version down the left, the sections of the one you are reading listed underneath it, and both halves are plain in-page links -- with the script blocked the rail is still a working table of contents over a page showing every release at once.
  • The roadmap reads as a board: an icon per item, a count per category, and categories that fold.
  • The front page's leaderboard picks the camera. While the arena card is showing a live match, each of its three ranked names is a button: press one and the picture cuts to that player or bot, seen from their own eyes. The row the camera is on is lit, and it is lit from what the match reports rather than from the press, so a camera that moves on its own -- the body being watched is fragged, and the view moves on after the fall -- takes the light with it. A name that is down when you press it is waited for briefly and picked up when it is back on its feet. With no match in the card the rows are what they always were: three names and their frag counts.
  • A watched match is only the match. The embedded preview used to carry the spectator's own furniture over the picture -- a wash across the whole frame, the word SPECTATING, and a line offering keys that the preview does not accept anyway. It carries none of it now. Those belong to a player who has no body and wants one back, and a visitor reading a web page is neither.
  • A bot match is a match, so the card offers Watch. A room with only bots in it was not being offered, which is exactly the room the always-on arena is.
  • The Story tab is gone. It read the district, the factions and the four playable dossiers straight out of the character data, which meant it could only ever say what a character file already said -- the setting has more to tell than a roster does. The nav is four tabs again, and a bookmark on the old #story falls through to About rather than dead-ending. The lore itself is untouched: it is still in the character files, and the game still reads it.
  • The key art is a picture rather than a band across the monitor, and the four character dossiers each lead with a painting of their own rather than a crop of the group art.
  • The always-on host keeps the backoff it logs. Three defects behind one set of logs: a page error racing the container's own boot work ended the process a quarter of a second into a five-minute sleep; the fatal it reported named data that was never at fault, because one transient relay failure rejected the whole art load and the boot screen swallowed it by design; and the heartbeat could not tell a stopped loop from a still picture. A boot stage is retried now, a stage the game cannot open a match without stops the boot on the screen that says which one died, and the heartbeat probes the page rather than comparing two canvas reads.

Known limitations

  • The desktop downloads are not offered. 0.8 shipped installers for Windows, Linux and macOS and linked them from the About page; the repository is private, and GitHub binds release visibility to repository visibility with no setting that separates them, so those links 404 for everyone who is not signed in with read access. The section is out of the page until the assets are published somewhere reachable. The workflow still builds and attaches all three on every tag.
  • No shipped arena is under weather yet, and a lightning strike washes the frame rather than lighting the walls. Both are described in full under Weather above.

0.8

the arena fills with monsters, and the game leaves the browser.

0.7 handed the room over to the person authoring it. 0.8 puts something in it worth fighting. Survival is the first mode that is not soldiers shooting soldiers: a co-op stand against waves of the dead, twenty drawn creatures, bosses that call up help while they live, and a squad that crosses the floor to pick you up when you go down. The arsenal stops being handed to you and becomes something you collect out of crates and off the bodies of people who were carrying it. Guns hold magazines and have to be worked between them. A building can be several storeys tall, an arena can open onto a sky, and three of the things you throw are machines that fight for you. And the game stops being only a browser tab: there is a build for Windows, Linux and macOS, and a phone can put it on a home screen.

Skies

  • Six more skies. The library used to hold four, and between them they covered weather: a noon sky, a night, a full overcast and a moon. None of them said what time it was and none of them said anything about the district being sealed. There are ten now.

dusk puts the sun on the horizon with a wide bloom around it, so one wall of an arena is lit gold while the wall opposite is already night, and which way you face changes what you see a body against. dawn is its morning: navy overhead, a cold cream along one edge, thin high cloud. sodium is the one that could only be here -- a sealed district still burns its own streetlights and a low deck throws every one of them back down, so the brightest thing in the frame is the ground, reflected, and there are no stars because from under that you would not see any. smog is daylight arriving through something, with no blue in the picture at any elevation. ashfall takes the sky away entirely and leaves the sun a red coin you can look straight at. storm is the only one brighter above you than at the horizon, which turns every skyline in an arena from a lit face into a black cut-out.

  • The night sky has a galaxy in it. starry shipped as an even scatter of stars, which is a night sky with nothing in it and nothing to steer by; it now carries a band arcing across the north-east where stars crowd and a mist of unresolved ones lifts the sky between them. Every heading under it looks like a different piece of sky.
  • Clouds bank the way clouds do. The cloud field was sampled at one scale on all three axes, so a deck came out as round blobs; a sky can now squash it vertically into layers instead. overcast deliberately keeps the old field: a flattened deck is a look, and one sky in the library should not be wearing it.
  • The sun does not have to be white. Every disc in every sky was pure white, which is right for noon and wrong for a sun seen through ash. A sky can name the colour of its own disc now; leaving it out still gives white, and the four skies that shipped before this render exactly the same texels they always did.

Survival

  • Survival. The game gets a second thing to do. Up to four of you hold an arena against waves of the dead, and it is co-op: everything in the room is hostile to all of you, and what the board counts is the wave you reached rather than who beat whom. YARD, PLAZA and HOLLOW are offered for it -- open ground with room to fall back across, rather than a corridor a wave can trap a runner in.

A wave spends a threat budget rather than counting bodies. Every creature costs its tier, one for fodder, two for a mid, three for a heavy, so the same budget buys a horde of shamblers or a handful of brutes and nothing can buy twelve brutes. The mid and heavy tiers unlock on how far into the run you are rather than on the wave number, so a short run meets heavies early and a long one holds them back. Arrivals are spaced out and the spacing tightens as the run goes on, because a wave that arrived all at once would be a wall rather than a wave. A finite run is won by clearing its last wave; an endless one keeps climbing.

  • Twenty creatures, and some of them shoot. The roster is drawn art rather than recoloured soldiers, and it lives in a library of its own apart from the playable cast: shamblers and runners at the bottom, hounds, scuttlers, floaters and spitters through the middle, brutes, maws, reapers and elementals as the heavies. Each walks, attacks and dies on its own sheet.

A creature can declare a ranged attack in its own file and take a firing pose for it, so a spitter spits and the bolt crosses the room. Until now every monster in the game had to reach you to touch you, and backing away was a complete answer to all of them.

  • A wave can arrive with a boss on it. A boss is one of the library's own creatures spawned harder: twelve times the health, half again the damage, and slower than it would otherwise be, so the thing that hits hardest is also the thing you can back away from. It never comes alone. An escort arrives behind it, and it calls up more while it lives -- five to ten fodder every five to twelve seconds, against a cap on how many summoned bodies can stand at once -- so standing off at range costs you the room you are standing in, and the wave cannot clear until the floor does.

Four creatures exist only as bosses and never turn up in an ordinary wave: the warden and the broodmother close on you, the censer and the oracle shoot. A boss is drawn at its def's own size rather than scaled up. The arena's walls are exactly one cell high, and a body drawn taller than a cell paints across the ceiling and through the room behind it.

  • A map says where a wave arrives. An arena marks arrival cells in its own legend, and the spawner draws from those rather than from the places a player starts, so a wave comes in at the edges of the room instead of appearing in the middle of the fight you are already having. A creature arrives with an animation rather than blinking into being. An arena that marks no cells is not offered for survival at all, which is checked against the map file rather than trusted.
  • You go down before you die, and your squad picks you up. Take the shot that would kill you in survival and you drop instead of respawning: the camera slumps toward the floor, you keep your view of the room, and there is a window in which somebody can reach you. Nobody comes, and you bleed out.

Somebody does come. A bot on your side breaks off what it is doing, crosses to a downed teammate and revives them, so a run with a squad in it is a run with company rather than one you lose the moment you are the last one standing -- and a standing bot counts, so the run no longer ends the instant the last human is on the floor. The scoreboard says who is out and who needs picking up.

The camera is the whole of it for now. A downed body was meant to draw its own pose to a peer as well, and no character declares one yet, so what everyone else sees is still the settled death cell.

Weapons

  • The axe. A tenth weapon, and the first melee one that leaves your hand: it swings on the trigger like a knife, or the aim button winds a throw and the trigger sends it. A thrown blade tumbles, bites what it reaches, and sticks where it stopped -- in a wall, in a door, in the floor -- drawn at the angle and the height it landed at, and it stays there as a pickup for whoever wants it back. A blade that lands somewhere you cannot reach stays there.
  • A thrown axe hits what it lands on. It used to reach bodies and nothing else: monsters, deployed machines, barrels, crates and shop windows all took the blade and ignored it. The throw looked perfect every time, because it was -- the axe flew, landed, stuck and left a pickup, and only the health of the thing it was buried in said the weapon had done nothing. A bullet from the same player reached all five.

A blade now arrives at a monster for the throw's own damage, at a deployed sentry or drone for the same, at a destructible prop the way a shot triggers one, and at a pane of glass, which it shatters and then drops to the floor of the hole it made rather than hanging in it. Your own machine is a target now, which it was not: it was filtered out by allegiance while your own bullet went into it happily, and an axe you cannot aim at the turret in front of you reads as a broken weapon. The guided rocket keeps that filter, because a round that detonated on the drone escorting you would kill the escort on every launch.

  • The thrown axe leaves the hand when the hand opens, and flies where you aimed it. Two things were wrong with the throw and both were about the picture rather than the flight. The axe's release frame is an open glove, and the blade used to appear four tenths of a second after that frame was drawn: you let go, the hand emptied, the room stayed empty, and then an axe materialised out of nothing after the throw had visibly finished. That delay belonged to the grenade, whose fire frame still holds the nade and whose hand opens a beat later; the axe's hand is open from the first frame, so its blade now goes with it.

The blade was also drawn a quarter of a cell above where it actually was. It leaves at chest height, which is eye height, so a level throw should cross the horizon -- instead it came in level with the top of a head while it bit at the chest, and aiming at what you could see was aiming high. Every other thrown thing is a grenade, a sentry puck or a drone, and those come to rest, so the renderer put the bottom of the art at the object's height. An axe never rests in flight, it turns, and the point it has to be drawn at is the point it turns about. A weapon now says which it is, in its own file, and the axe's eight tumble cells are cut around the middle of their cell to match.

  • A thrown weapon flies in three dimensions. Every throw in the game used to be resolved against the floor plan alone, with no height in it at all: the arc you watched leave your hand was a picture, and what the game tested was a dot crossing a map. Lob a grenade over a wall a cell in front of you and it struck the wall's footprint in mid-air, well above the bricks, and came back at you. Indoors it failed the other way up, because a grenade peaks higher than a ceiling is tall: every indoor lob rose up through a solid roof with nothing to stop it.

A throw is tested at the height it is actually at now. A blade flies against the arena's real heights, clears a wall it has visibly cleared, and hits only what is in the air with it; one that comes down on a roof stays up there, out of reach, rather than dropping back into play. The grenade, the sentry and the drone are on the same rule, and it is the grenade the rule matters most to: it clears a one unit wall about a cell out and lands four cells past it, which is the range you actually fight at. A machine that settles on a roof knows it has landed and deploys there, instead of counting itself airborne forever and quietly never arming.

  • A rifle, and something to look through. The eleventh weapon is a marksman rifle in slot 3, beside the shotgun and the chaingun, and the right mouse button puts your eye behind its scope: the rifle comes up through two poses, the world magnifies more than three times, the gun fades out of the frame, and a scope picture takes the screen. It fires about twice a second and a headshot never quite kills from full health, so a long sightline is an advantage rather than a verdict. It feeds from the chaingun's belt, which means the two share one reserve and carrying both halves each. Crates hand it out on the everyday table, so it turns up in most fights, and gun game gains a rung for it.

The scope is not the rifle's alone. Any weapon that already aims down sights can declare one in its own data, name a picture, and say how far into the raise the picture arrives.

  • Aiming down the sights magnifies the world. The aim button used to raise the gun and change nothing else; every weapon that has sights now zooms behind them, by an amount it names in its own file -- a little over one and a half times on the chaingun, nearly twice on the pistol, and more than three on the rifle's scope. Sights are worth using at a distance now rather than being a pose the viewmodel plays.
  • The launcher. An eighth weapon, and the only one with something on the aim button that is not a pose: hold aim and a bracket converges on whatever is ahead of you, and nine tenths of a second later the lock closes. Click and the missile steers itself onto that target for as far as the lock reaches, twenty-four cells. The lock is not spent by firing and it is not lost by looking away -- once it has closed it stays closed until you let the aim button up, so you can put a second rocket onto the same target as soon as the tube is clear, and a target that ducks behind cover is still yours. Let the aim up and it bleeds away in under half a second. Fire without one and the rocket flies the heading it was given, which is still a ninety-damage blast wherever it lands -- the round is spent either way, so the weapon never swallows a shot. It locks onto players, bots and turrets, and never onto a teammate or onto scenery. Lose the target mid-flight and the missile stops steering and flies on. It leaves a smoke trail that billows and rises behind it -- puffs of different size and shade drifting off the line instead of hanging in a straight row -- and its motor is a voice that travels with it across the arena and stops when the round does. You start with two, carry five, and a box drops one. It is off the gun game ladder. Bots use it as well: a bot has no aim button to hold, so it launches at whatever it is already shooting at. The round itself is not drawn yet, so what you follow across the arena is the smoke and the motor rather than the missile.
  • The missile hunts its target instead of curving onto it. The round builds a turn, carries past its aim and comes back, so the path it draws is a weave rather than a smooth arc -- and the smoke behind it lasts long enough for a whole swing of that path to be on screen at once. The swing itself is small, about three degrees of it, so what reads is the shape of the trail rather than the round visibly slewing: a shot taken close, or one taken with the target out at the edge of the bracket, shows it best, and a long shot down the middle converges before it has room to swing. It still catches what it used to catch. A body strafing across the shot or running in at an angle is hit where it was hit before, and a target standing still is hit exactly as it was. A round that loses its target curls out of its turn, flies on and blows up where it lands, the way it always did.
  • The sentry. A seventh weapon, and the first one that fights for you: throw it, and wherever it stops rolling it plants itself, clunks live a second and a half later, and shoots any hostile body inside nine cells. It turns to track, it misses a little, and it ticks twice a second until its forty rounds are gone. It is a target as well as a shooter -- a hundred and fifty health, and it takes its own blast with it when it dies, so standing beside your own turret is a decision. It keeps fighting after you are killed, every peer sees the same machine, and bots pick one out and shoot back at it. You start with none, and a sentry box is a seventh kind of drop on the arena floor, so carrying one at all is something the map decides.
  • The drone. A ninth weapon, and the sentry's airborne cousin: throw it and it arms and shoots the same way a sentry does, but it never touches the floor. The device leaves your hand and opens at the top of its arc, a fifth of a second later, then eases down to the head height it holds -- so it is a machine while the throw is still going, and there is no moment of it lying on the ground. It follows you, bobbing gently as it hangs there, closing in whenever you put too much ground between you and holding a couple of cells off once it catches up, so it reads as an escort rather than a fixture. It still will not go looking for a fight on your behalf -- it closes on a target only as far as its leash from you allows, so if the fight moves away from you, the machine does not follow it there. It dies the moment you do, and quietly: no blast, unlike a sentry destroyed in a fight, so there is nothing left standing over your body. It shares slot 4 and the throw with the grenade and the sentry, and a box carries one, the cap two escorts already fill. The world art is a placeholder for now -- a painted disc, a barrel stub and a tick of legs standing in for a render nobody has made yet. The viewmodel is its own now: the idle, fire, unpin and recover hand poses are cut from a real render rather than the sentry's sheet copied wholesale. The crosshair and the three debris gibs are still shared with the sentry's own art, and stay that way by design -- both are small enough (the crosshair a single pixel, the gibs 8px shapes) that a downscaled render could not improve on them. The thrown device itself is the one still waiting: it should be cut from its own deploy render, which does not exist yet.
  • The drone's camera. A small panel opens in the corner of the screen the moment a machine you deployed opens up, showing what it sees from its own eye height rather than yours -- a doorway you would walk through without a second thought fills its frame. It comes up scrambled: a warm-up sweep plays across the noise for the second or so the device takes to arm, and the picture settles clean the instant the machine goes live. Whatever it has picked as a target -- the same body stepSentry names before the machine opens fire on it -- tints red inside a bracket on the panel, so you see the call rather than having to guess it. The picture repaints fifteen times a second, against the main view's sixty, and holds its last frame between repaints rather than blurring, reading as a security monitor and not a second viewport. Taking a hit tears it: a burst of static and a torn scanline cross the picture, worse the larger the hit was against the machine's own health, and settle back toward clean in well under a second if nothing else lands. Losing the machine holds the picture at full static for a beat before the panel goes dark, instead of cutting out mid-frame. The machine's own body in the world is still the placeholder disc, barrel stub and leg ticks it has always been -- the feed is a second raycast over the same geometry, not a reason that art moved.
  • The number row picks a slot rather than a weapon. Each weapon declares the digit that draws it, several weapons share one, and pressing the digit again walks them: 3 is the shotgun and the chaingun, 4 the grenade, the sentry and the drone, 5 the flamethrower and the launcher, with the knife on 1 and the pistol on 2. A weapon you cannot draw is stepped over rather than landed on, so a press never leaves you holding a gun with nothing in it, and a slot with nothing to give blinks the ammo cell the way a dry weapon always has. The status bar's icon block follows: one icon per slot, showing the weapon that digit would draw next, with a small mark in the corner when the slot holds more of yours. On a phone the weapon bar is one button per slot for the same reason, and a repeat tap walks it -- and it has buttons at all now, where the row was being built before the weapons existed and came out empty. The mouse wheel still steps one weapon at a time through everything you carry.
  • A weapon carries a magazine. Four guns now hold a fixed number of rounds and have to be worked between them: twelve in the pistol, fifteen in the rifle, fifty in the chaingun, and eight in the shotgun, which feeds one shell at a time so breaking off a partial top-up is a real decision. The magazine sits on top of the reserve rather than coming out of it. R reloads, a gun fired dry reloads itself, and switching weapons cancels it. The rifle and the chaingun share a reserve but never a magazine, so carrying both does not mean reloading both.

A reload says so at the reticle rather than in the corner of the screen: a ring fills around the crosshair, a per-round reload draws a pip per shell so you can see how many are in, RELOADING fades in underneath, and the ammo cell turns gold. It tells the other side too. A reload is a sound, and anyone close enough hears the gun being worked and knows you are not shooting for a moment. No reload art was drawn: a weapon with no frames of its own dips out of the frame and comes back, which is what let this ship without touching a sheet.

  • Weapon-owned wall impacts. What a shot leaves on a wall -- the burst, the sound and the mark -- now belongs to the weapon that fired rather than to one shared ricochet. The flamethrower stops zinging like a bullet: flame splashes where it lands, it whoofs instead of pinging, and it chars the wall instead of chipping it. Every other weapon marks a wall exactly as it did.

The arsenal

  • The arsenal is something you collect. You spawn with a knife and a pistol, and every other weapon comes out of a crate you shoot open or off the body of somebody who was carrying it. A crate carries a stencilled label, enough to tell it apart from ordinary scenery at a distance, but not enough to read which weapon is inside -- for that you need to close to within about five cells, because the crate itself is small and there is little face left to stencil on. What is inside is rolled from the arena's own tables, and the roll happens again each time the crate reassembles, so opening the same crate twice is not the same crate twice. Dying costs you the lot: you come back with the knife and the pistol, and the weapon you were holding stays where you fell, with whatever was left in it, for anyone who wants it. An opened crate reassembles about twenty seconds after its contents are gone. A custom arena that places no crates arms you with everything, exactly as before.
  • A broken crate leaves the weapon, not a box. Shoot one open and what is left is a socket in the floor with the gun sitting in it, drawn as that gun, so you can tell from across the room whether the crossing is worth making.
  • The pickups are objects. The ten drops used to be flat rectangles with an icon painted on them. Each is now a drawn object standing on a turquoise pad, cut from one authored sheet and snapped to the palette the rest of the art uses. Each carries eight views, and the game picks the one that matches the angle you are looking from, so a pickup turns as you walk around it the way a directional prop does.

Taking a shot

  • A shot that lands on a soldier's head is a headshot. The pistol is the one weapon that pays extra for it, at two and a half times its usual damage -- every other weapon does the same either way, and a headshot can only come from a shot -- a blast, a fire already burning you, or a bot's shot never is one, on bots or players. When a headshot is the kill, the kill feed marks the line with a small asterisk between the weapon icon and the victim's name; a headshot that does not kill says nothing. Aiming is stricter everywhere now too: a shot that used to land just because it clipped a soldier's box now has to land on the soldier, and a shot that only grazes the transparent edge of the sprite misses like it should have all along.
  • A fresh body cannot be shot. Spawn, and nothing can hurt you for two seconds or until you fire, whichever comes first, so respawning into somebody's line of fire is no longer a free frag for them. It is visible from both sides: you assemble out of the same stipple a billboard fades through, then shimmer while the window lasts. Bots respect it -- they will not pick, chase or shoot at a body they cannot hurt, rather than emptying a magazine into one. A fire you walk into still burns you: immunity is to shots, deliberately, and not to the room.

Bots

  • Easy bots are easy now. A bot's aim used to be exactly as good at twelve cells as at two, because the only thing that made it miss was a random wobble that never grew with distance; it now widens with range, so a bot is as poor at a distance as you are and a bot in your face is as dangerous as it ever was. Difficulty also decides what a bot's shot costs you and how fast it can shoot again, which it never did before: an easy bot hits for a little over half what a hard one does and takes almost twice as long between shots, so there are gaps in the fire to move through. Normal sits between the two, and hard hits exactly as hard as it always did up close -- it only loses the shots it used to land from across the arena. The whole ladder is in game.yml now, so the numbers can be tuned without touching code.
  • The arena decides how many bots may chase you. A bounded number of them can hold a claim on any one target; a bot that cannot get one stops chasing and keeps shooting, so a roster cannot pile onto whoever it saw first. Measuring it turned up something worth saying out loud: a roster does not dogpile in the first place. A bot picks the nearest body it can see, and in a free-for-all another bot is nearly always nearer than you are, so the roster spends most of a match fighting itself a few feet away from you. The cap is a ceiling for the rare convergence rather than a number that shapes an ordinary fight.
  • The bot console command takes verbs now: bot add n [level], bot remove name, bot remove_random n, and bot set [name] level to retune one bot or the whole roster, with a bare bot listing it. Each bot carries its own difficulty (bystander, easy, normal, hard), so a hard bot and a bystander can share an arena. The old bot n form is gone.

Fire

  • Being on fire is drawn in pixels, and it gets in your way. The fire on your own screen used to be a translucent orange gradient with seven smooth curves swaying across the bottom of it: a sine wave in a game whose every other surface is a hand-placed texel, and thin enough to see straight through. It is now a cellular fire simulated on a grid a quarter of the framebuffer in each direction, so every pixel of it is a square block on the same lattice the sprites sit on, painted in five tones taken from the pyre and flame sheets themselves. Nothing about it blends: a pixel is either a tone from that ramp or it is the room behind it, and where the fire thins out it breaks up into a checkerboard rather than fading, the same trade the puff rims already make.

It also covers two states now rather than one. Catch fire and live, and flames take the bottom third of the view: enough to lose your footing and the floor of the room, not enough to lose the horizon you aim along. Die in it and the fire climbs nearly the whole view and runs up both screen edges, which the old version could only suggest, and it burns out on its own rather than being crossfaded to a grey rectangle. The tones live in game.yml beside every other colour the game paints with.

  • Fire follows you out. The flame's reach is shorter than it was: it burns what stands in it rather than what walks past it, so a corridor with a fire in it is passable again. Crossing one is not free, though. Whatever the flame touches keeps burning for two seconds after it is clear of the fire, the same way a flamethrower's spray does, and standing in the flame keeps relighting it. Bots burn on the same terms, which they did not before: until now a fire was a hazard only a human could walk into.

Buildings, and the arenas around them

  • A building can be tall. Until now every wall in the game was exactly one unit high, so an arena was a set of sheds standing against the sky. A map can now raise a run of solid cells to several storeys, and the yard is the first arena to use it: the blocks you fight around rise five and six floors, and the wall at the edge of the map rises into a city that stands behind them. What a storey wears is a facade kit, named per rect rather than derived from the material, so the same brick builds a shopfront on one map and a tower on another. Four kits ship: office windows under a parapet, a brick shopfront, a glazed tower whose band you see the next building through, and a roomless variant of the first for distance.

The upper windows are not painted on. They are the same virtual rooms the shopfronts at ground level already use, one per window, chosen by hashing the cell and the storey, so a block's lit and dark floors are scattered rather than repeated and every player sees the same building with nothing on the wire. A storey too small on screen to read draws the flat band instead, which is what keeps a skyline affordable.

Nothing about the fight changes. A raised cell was already solid, already stopped a bullet aimed at any height, and already hid whatever stood behind it. Light does not gain a third dimension either: a muzzle flash in the street lights the shopfront and leaves the tenth floor alone, which is the storey fade doing on purpose what the lightmap cannot do at all.

The map editor gains a Height layer beside Floor, Walls and Sky, with its own brush for the storey count and the kit, the same rect drag and Shift-drag erase the other surface layers use, and a wash that labels every raised run with what it is.

  • An arena can open to the sky. A map can mark its edge see-through and put a cubemap behind it, so the boundary of the world is a horizon rather than a wall you walk up to, and a see-through wall has a ceiling that opens with it. A shot fired into open sky is silent and leaves no mark, because there is nothing up there to hit -- it used to ping off the sky like a wall.
  • Two more arenas. HOLLOW is a wood you can see the stars out of, open ground under an open sky. BACKROOMS is a mirrored maze that gives you the same room over and over with no landmark to fix on.
  • Multi-floor arenas. STACK is two floors in one arena, linked by three lifts and by nothing else. The camera settles as you ride up, the minimap shows the floor you are standing on and names it, and a firefight on the other floor sounds like it is through a floor rather than through a wall.
  • Lift portals. A portal can now be a lift instead of a shimmering ring: its mouth shows a car receding into the wall, built from the same cutout planes a shopfront uses, and walking in still takes you to the far end. threshold's blue and green pairs are both lifts, and its orange pair stays the see-through kind you look through.
  • Portal traversals fade. Crossing between floors fades through black with the cut hidden at the darkest point, and nothing parks you anywhere on the way through. Every lift mouth carries chevrons pointing the way it goes.
  • Walking through a see-through portal no longer fades the screen. A pair you can see through already shows you the far side, and the view carries straight over as you cross, so there was never a cut for the fade to hide -- the black frame and the whoosh were the only things telling you a crossing had happened at all. You step through one the way you step through a doorway now. Lifts and stairwell mouths are unchanged: they are solid, the far side arrives with no warning, and the fade is what makes that arrival read as a ride rather than as a glitch.
  • A portal mouth takes no bullet marks. Shooting a lift mouth used to leave scars hanging in front of the room behind it, which read as a pane of glass across an opening you are meant to walk straight into. The mouth stays clean now. The other three sides of the cell are ordinary wall and scar like it.
  • Chainlink fences. Six new wall materials in the metal group: a diamond mesh on its own, capped with rails, or with a line post, each in galvanised or rusted. A fence stops you and stops a shot the way any wall does, but you see the fight through it, so a boundary no longer has to be a blind one.
  • Chainlink comes in two mesh sizes. The six fences now also exist at twice the density, twelve materials in all, and the wire thins with the weave so a tighter mesh is the same fence at a smaller scale rather than a darker one. A fine mesh hides more of what is behind it without ever becoming a wall.
  • Deeper shopfronts. An interior kit carries any number of cutout planes instead of one, declared in its kit.yml as a depth and a file, so a window shows several fixtures parallaxing against each other and against the back wall. A plane may be marked unlit, which lifts it out of the room's depth shading: that is how a lit sign reads bright at the back of a dark shop. Four of highstreet's shops are rebuilt on it.
  • A door can hang mid wall, near end or far end. Until now every door's slab sat centered in its cell; a placed door can now be pushed toward either wall face, so a blast door can sit flush with the doorway it guards instead of floating in the middle of the wall it is set into. The depth is set per placed door, with R in the map editor, and the canvas draws a bar showing where the slab hangs. Where a door blocks and where it triggers are unchanged -- only the slab's resting position moves.

Props

  • The props are drawn by hand. The library used to be generated art, each piece rendered on its own and sharing no palette with the rest, and a room of it read as a pile of separate renders. Every prop is now redrawn against one master palette, so the scenery in a room reads as one set of objects. Generating them was built, measured and retired: a 512px render carries photographic detail that survives the downscale to a cell as noise, every take passed every gate the tool could compute, and legibility is not one of them.
  • A prop can hold a sound and play it forever. A prop names a looping clip in its own file and plays it from where it stands, attenuating with distance like anything else in the world, which is how a fire crackles for exactly as long as it is burning.

Characters

  • Two more characters. A hazmat suit in high-visibility yellow behind a blue faceplate, and a civilian in a tee and jeans. Each recolors the same five ways every character does, but the screen now calls each part what the character actually wears: the hazmat's rows read VISOR, HOOD, SUIT and LEGS, and its suit and its legs take separate colours, so a two tone hazmat is a thing you can build. Bots draw from the same roster, so a match fills with all four.

Sound

  • More of the arena makes a sound. Picking something up has its own cue, one each for a weapon, for ammo and for health. A body catching fire and the fire going out are both heard. A sound from another floor plays muffled rather than arriving at full strength through a ceiling, so a firefight upstairs reads as upstairs. Ten clips that were a synthesized placeholder tone are now authored recordings.
  • Two more tracks in the soundtrack. GRID RUNNER and MIDNIGHT PREDATOR are two minutes of synthwave each and sit with the other retrowave cuts. Both are instrumental, so neither carries a LYRICS chip, and both are browsable in the JUKEBOX and reachable from its shuffle. Neither is wired to a map or a menu, so no arena or menu theme changes.
  • Two more tracks in the soundtrack. NEON DUSK is three minutes of synthwave and sits with the other retrowave cuts; DEAD FACTORY is four minutes of ambient and sits with the arena beds. Both are instrumental and both are browsable in the JUKEBOX. Neither is wired to a map or a menu, so nothing that played before plays differently now.
  • GHOST PROTOCOL, the track the menu opens on, is a longer cut. It runs two minutes rather than one minute twenty, so the shell plays further into it before it repeats. The id, the title and the file path are unchanged.

The tools

  • The weapon editor. A weapon's numbers now have a tool: tools/weapon-editor.html opens any of the eleven weapons, groups its yml into the things you tune together, and puts a live viewmodel beside them. Hold the trigger and the gun fires at the authored rate, on the authored cells, throwing cases from the authored port. The timing is not a model of the game's, it is the game's, called straight out of combat.js and the shell pool, so a fire rate that reads well in the editor reads the same in a match. A strip under the gun draws the shot's beats against the cooldown, which is what turns a frame starting at 0.4 into something you can see rather than guess at.

Saving rewrites only the lines that changed. The library carries a lot of hand-written comment explaining why its numbers are what they are -- the rifle's eject block is a good example, a long explanation of why its ejection port sits where it does rather than where the art draws the opening -- and an editor that rebuilt the file would have deleted every line of it. Before it hands a file back, the editor re-reads what it just wrote and compares it against what it meant; a disagreement refuses the export rather than shipping it.

  • The weapon editor drives both mouse buttons, per weapon. It used to fire on the left and raise sights on the right for all eleven weapons, which is wrong on two of them and silently so: the axe's trigger winds a throw and its aim button swings, and the launcher's aim closes a lock rather than raising sights it does not have. Press the right button on the axe and nothing happened at all. Now each weapon's buttons mean what they mean in the game, and the hint under the stage says so for the weapon that is open.

That also reaches four poses the tool could never draw. arm and throw on the axe and sight and locked on the launcher are picked by rules rather than named in the shot schedule, so nothing in the editor had ever shown them. The wind-up's draw-back and the throw's follow-through were pinned at zero and are now the game's own curves. R plays the reload, which is the only picture the reload block's numbers have ever had. A readout beside the timeline names the cell on screen, including when it is the idle fallback standing in for art the sheet has not got.

  • The prop editor. The thirty-five props now have a tool of their own: tools/prop-editor.html opens any of them and edits both halves at once. The sheet is painted texel by texel on the master palette every prop is drawn from, with a strip above the art showing every cell and chips saying which of idle, breaking, broken and debris claims it; a chip refuses a claim the parser would refuse, so a prop is stopped at the strip rather than at the export. One cell copies over another, which is how a breaking frame starts life as a bruised idle one, and the paste undoes like a brush stroke. The sheet is read and written as PNG bytes rather than through a canvas, so an export carries back exactly the texels it opened. The metadata is the same file's numbers as grouped fields over the rules propdef.js enforces, and the prop stands beside them on a lit stage at a distance and a facing you pick, breaking, fusing or venting on the game's own frame clock over a real lightmap bake. Its sounds play from the page, and an .ogg you drop on a clip field auditions from the file itself. Export rewrites only the lines that changed, so the library's hand-written comments survive, and two guards refuse the file if the patch changed what it means or touched a line nobody edited. New makes a prop from nothing: an id, a cell size and a frame count give a blank sheet, a document that parses and a props.yml with the id appended. What the stage does not do is throw debris: gib launch, the baked spin and the stipple fade belong to the particle system, so the debris cells are shown as a strip and labelled as exactly that.
  • The wall editor. A wall material is a stack of layers, and there is now a page that shows it as one: tools/wall-editor.html opens a material, draws its layers over a tiled panel so a seam is visible while you work on it rather than after you ship it, and writes the file back.
  • The character editor edits pixels. It used to place parts and recolour them. It now has a second surface where the sheet is painted texel by texel on the master palette, with the tone strip doubling as the picker and the mask keeping up as the brush moves. A cell can be copied and pasted over another, carrying its art, its claim and its head circle with it, and the paste undoes like a stroke. It opens any PNG from disk rather than only a converted sheet. Defringe erases the plate bleed a converted sheet carries around its edges, anchored on a tone you pick rather than on white. The rail is built from data now, fits the page, and its buttons wear icons that say what they do.
  • The map editor gains layers. A ceiling has its own textures and its own tool beside Floor, Walls and Sky, the rail's tile groups collapse so a long list stays navigable, and the ceiling tool names its Shift-drag erase the way every other surface layer does.

The site

  • The arena widget on the front page can be watched, not just read. When a public match has players in it, the card offers a Watch button, and pressing it embeds the game itself, spectating that match from a player's eyes. It is the same spectator the game has always had, so nothing new goes on the wire: the page joins as a watcher with no body, appears in the roster, and takes no part in the fight. It is silent, it never starts on its own, and it comes down when you press Stop, when the last player leaves the match, or when you leave the tab.
  • An operator can stream a live match to YouTube. A sixth container in host/ joins a room the always-on hosts keep open, as a second spectator, and OBS pushes what it sees out as a broadcast: the arena exactly as a drop-in player would find it, with the sound the spectator camera hears. It is opt in by compose profile and refuses to start without a stream key, so nothing goes live by accident. Nothing about the game changes.
  • The site remembers a theme, and it opens dark. The nav row ends with a switch between light and dark; the choice follows you from page to page and across tabs, and a first visit gets dark rather than whatever the operating system asks for. The game and the editors have no switch of their own yet, so a visitor who deep-links straight into one gets the default.
  • The game installs to a home screen. A phone or a desktop browser that supports it is offered an install, and an installed copy launches with no browser chrome around it: the arena fills the screen and nothing else is on it. On a notched phone the view keeps clear of the cutout. iOS offers no prompt, so there it is Share, then Add to Home Screen, by hand.
  • A missing page is a dead end in the arena rather than a bare server error.

Off the web

  • The game is downloadable. There is a build for Windows, Linux and macOS, each one a window of its own around the same game the browser runs, so the bots and survival mode come with you when you are offline. Multiplayer still needs a connection, because finding your friends does, and it is the same game either way. The builds are not code signed: Windows warns about an unknown publisher the first time you run the installer, and macOS refuses a plain double click -- right click, pick Open, and the dialog gains the button that lets it through. You do that once. Tagging a release builds all three and attaches them to it.

Content, not code

  • UI and HUD colors move into game.yml. Every tone the shell and the HUD paint with is a key under colors:, falling back to the same value in code, so a retheme is a data edit. Nothing changes on screen.
  • The cast carries a back story. Every character's file now holds a name, a title, a faction and a few lines on where they came from and what they think they are doing, validated the way the rest of a character is. Nothing in the game displays any of it yet: it is the content half of a screen that has not been built.

Networking

  • Typed chat. T talks to the room, Y to your team, U opens the log. Lines land as a short fading stack over the arena and stay in a panel you can scroll back. Touch players get a CHAT button. OPTIONS > GAMEPLAY > CHAT turns the whole thing off. Open on the pause screen, chat still owns the keyboard, so Escape closes it instead of resuming the match and the arrows scroll the log instead of moving the pause selection, the same way the developer console does. The protocol moves to 18, so a client on 17 lands in a different room.
  • The wire protocol is at 32, having moved fifteen times across this release as survival, crates, slots, aimed throws and the rest each added to it. A client on an older number lands in a different room rather than in a desynced one, so everyone in a match is always speaking the same version.

Known limitations

  • Nineteen of the sixty-five gameplay clips are still a synthesized placeholder tone. PENDING_AUDIO_ASSETS.md carries a brief for each, though its own header count is stale -- the encoding is what says whether a clip is real.
  • Bots do not reload. Magazines are a rule the human half of the arena plays by, so the bot ladder has not been measured against the version of the game that ships here.
  • A downed body draws no pose of its own. The camera slump ships and the art does not, so a downed teammate reads as a corpse to everyone but themselves.
  • The drone's body in the world is a placeholder disc, barrel stub and tick of legs, and the missile round is not drawn at all -- what you follow across the arena is its smoke and its motor.
  • There is still no working TURN server. Two peers who both need a relay find each other and then never connect, and the arena reads as empty.
  • Assault is in the mode list and still locked.
  • Portals connect two points inside one arena. Connecting separate maps into one world is designed but not built.

0.7

the arena becomes content.

0.6 filled the room with light, glass, doors and voices. 0.7 hands the room over to the person authoring it. Glass stops being a solid block of a cell and becomes a panel standing on one edge of one, with floor on either side. Transparency and breakability stop being facts about a material and become decisions a mapper makes cell by cell, and so does the shop behind a window. The last art registry living in engine source moves out to yml, the sounds that hung off no file at all get one, and everything the browser fetches at runtime pools under a single directory. What is left in JS is the game; what stands in front of it is content.

Screens

A wall material fills its cell, and so did every pane of glass in the game: walk up to a shop window and you stop at a block of glass a metre thick. A screen is a panel standing on one edge of its cell instead. The rest of the cell is floor you can stand on, and the panel blocks its own plane and nothing else, which is what a shop window, a balcony rail and a glass partition have always wanted to be.

It is one authored key on a cell, screen=<dir>. The editor sets the facing from the cell menu and draws a corner badge for it, the map format round-trips it, and the parser refuses it on a material that cannot be a pane and on a cell that already carries a room. The renderer draws the panel on the boundary it stands on, from the front, from behind and edge-on, rather than on the face of a whole cell.

Rendering and collision agree because they read the same table. A boundary between two cells either carries a panel or it does not, and walking, a grenade's flight, a blast's reach, a bot's path and a line of sight all put that one question to it. So a bot paths around a panel rather than through it, a grenade bounces off the glass you threw it at, and the nudge that separates two bodies cannot push either through a pane.

PANELS joins the arena list to show them off: four sheets of glass standing in open floor with room on both sides of each, and nothing else in the map to look at.

Glass, cell by cell

A map can say something about a single wall cell now, beyond which material it is. An overrides: block lists the cells that depart from their material: a glazed cell is seen through where the material is opaque, and a breakable one can be pinned solid where the material would otherwise shatter. The raycaster, the lightmap and the breakables all read the departure, so a glazed facade lights, draws and breaks as the glass it now is.

The two glazed shopfronts are repainted to match. Their panes carry alpha and their frames do not, so a see-through cell reads as a window in a wall rather than as a tinted slab, and a real room carved behind one is visible instead of being marked unreachable.

A material also declares its own sidedness. A run of glass reads as one sheet rather than as a stack of tints, which is right for a nine-cell shopfront and wrong for a glass box, whose far wall used to vanish because the near wall claimed the column. The wall's own file settles which it is.

The rooms behind the windows

A room is authored on a cell, not derived from one. In 0.6 the shop behind a window came from the material's kit set plus a hash of the coordinates, with a map-wide pin to override it; a mapper could not simply say which shop sits behind which pane. Both mechanisms are gone. A right-click on the editor canvas opens a menu of kits, the choice lands in the map as a coordinate and a name, and a badge marks the cell that carries it. A cell with no assignment shows nothing.

The shipped arenas are unchanged on screen: the values the hash produced were frozen into HIGH STREET and POLICE STATION exactly as they were, so the same shop stands behind the same window as before.

The cell menu is where the other per-cell decisions live too, in three sections: the room behind the pane, whether the cell is see-through, and whether it breaks.

The arsenal strip

The status bar shows what you are carrying. The weapons you own draw as a row of flat icons on the panel, the one in your hands lit brighter than the rest, and a weapon you have just picked up flashes gold for half a second so an acquisition in the middle of a firefight is something you see rather than something you discover later. A weapon with no ammo left dims. Weapons you do not own are absent rather than greyed out, so gun game's single weapon sits alone in the middle of the strip and a full loadout fills it.

Weapons

A weapon's viewmodel timing comes off the weapon. The sequence of frames a shot plays, and how long each holds, is authored in weapon.yml instead of being one of two tables in JS picked by whether the sheet happens to carry an unpin cell. The flamethrower is the reason: under a held trigger it flickered through frames timed for a pistol, and it now holds a steady jet for as long as you hold the key. Recoil is authored the same way, a kick and a settle per weapon, so a fast weapon leans into its burst instead of sawing through it.

The mouse wheel switches one weapon per flick. A detented wheel reports several notches per gesture, and switching on each of them meant a flick up and a flick back did not return you to the weapon you started on. Notches that belong to a flick already spent are dropped now, and a switch that is interrupted resumes from where the weapon actually is on screen rather than snapping to the bottom and dropping again.

The comms wheel

A pick on the comms wheel says the line you picked. Two gates stood in the way, both silent: the bus refused a callout while another was still sounding, and a six second floor per speaker refused for seconds after that. A soldier cuts themselves off on a pick now, the floor drops to a debounce, and the wheel greys out for the window that still refuses, so a key that will not fire says so. The gates that matter still hold: a frag shout cannot talk over the callout you chose, and a teammate's callout no longer mutes yours.

The voice bank is re-rendered against what each line says rather than against how long it runs, which retires the clips that spoke something other than their caption.

A body spawns facing the room

A spawn point says where a body stands and never which way it faces, so a fresh life started looking at +x and a respawn kept the facing the corpse died with. On the shipped arenas that is a wall within three tiles for 29 of the 81 spawn cells. The facing is read off the geometry now: the eight compass directions are probed with the body's own collision, the ones with three tiles of floor ahead survive, and one of those is picked at random, so two bodies spawning on one cell look different ways while a corridor still offers only its two ends. Players, bots and the parked spectator camera all use it.

Getting into a match

Every arena declares the modes it is offered for. The map rail filters on the manifest without loading a single map file, and RANDOM draws from the same filtered list, so a mode never offers an arena it cannot be played on and never withholds one it can. Claiming capture the flag is a claim that the arena marks a base for each side, and the suite checks the claim against the map rather than taking it.

RANDOM rolls again on every rematch. A room founded on RANDOM used to resolve one arena and then play it until somebody left to the menu; the pick is remembered now and rides the referee's snapshot, so each match after the results board is played on a different arena, drawn from the arenas the room's mode offers and never the one just played, for everybody in the room at once.

The map editor

Play goes straight into the map. The editor's Play button used to hand off to the front door, which meant the boot intro, the room code and the bots screen between every edit and the thing being edited; it founds a room of one and loads the arena directly now, and EXIT comes back to the editor with that map open, whether the tab it came from is still there or not.

The canvas gains the right-click cell menu described above, with a corner badge for each of a room, a glazed cell, a fixed pane and a screen's facing, laid out so they do not overlap at low zoom. The kit menu scrolls with the wheel instead of closing under it, and erasing is the floor brush rather than a modifier key.

Content, not code

Wall materials are files. Every material was a JS object literal in a long array in the art pack, which made adding a wall, or giving one a new property, an edit to engine source; each one is game-data/art/walls/<group>/<name>.yml now, beside its own art, exactly as props, weapons, doors and interior kits already were. The game, the editor and the art export all load the same corpus, and the migration changed no material: every record comes out byte identical.

The clips that hang off no def move into game-data/config/game.yml: the boot intro sounds, the menu blips, the flag cues and the portal whoosh, which were four hardcoded path tables in JS. They come from one cue registry seeded from that file, along with the boot intro's logo hold, which was a constant.

The shipped data pools under one roof. art/, audio/, maps/ and config/ move under game-data/, which leaves the repo root saying which directories the browser fetches at runtime and which are authoring sources that never ship. Names, formats and layout inside those trees are unchanged.

Networking

Protocol stays at 17. Nothing on the wire changes shape this release, so a 0.6 client and a 0.7 client still land in the same room.

TURN gets a TLS fallback. Both configured entries asked for UDP, so a network that blocks UDP outright had no relay at all, and a turns: entry on 443 is what an egress filter passes as HTTPS. It is necessary and not sufficient: a probe that gathers with relay-only candidates shows the public Open Relay endpoint returning nothing on any entry, which means the mesh has had no working relay regardless of transport. That is what a player behind symmetric NAT or CGNAT has been hitting, and it reads on screen as an empty arena.

npm run turn-check is the probe. It asks each configured server for a relay candidate directly, runs two controls first so a dead network cannot be mistaken for a dead server, and takes a candidate provider on the command line so a replacement can be tried before it is committed. The background in plain language is in docs/networking.md, the findings and the options in the roadmap, which now carries the open question of whether the transport stays peer to peer.

Odds and ends

The console scrollback walks with alt+up/down, because macOS reserves ctrl+up/down for Mission Control and the scroll never reached the game there. The mouse pointer is hidden only while it is captured, so it behaves normally on the boot screen, in the menus and after Esc. The jukebox drops the caption under its mode row, the notes listing on the site drops its release dates, and the flamethrower's spritesheet and the hero's art are both redrawn.

Known limitations

  • Twenty of the thirty-four gameplay clips are still a synthesized placeholder tone: the six door pairs, the fire and glass effects, the prop break, the portal traversal, and the four boot intro sounds. PENDING_AUDIO_ASSETS.md carries a brief for each.
  • There is no working TURN server. Two peers who both need a relay find each other and then never connect, and the arena reads as empty. Replacing the provider heads the networking section of the roadmap.
  • Screens and per-cell wall overrides ship in PANELS and in the editor. No other shipped arena authors one yet.
  • Two of the six door types are placed in the shipped arenas: the default sliding door, and the roll shutter on the west bay of the arcade mouth in HIGH STREET. The other four are authorable in the editor and the map format but appear in no map.
  • Survival and assault are both in the mode list and both locked.
  • Two characters. HERO and POLICE OFFICER are the shelf, and a new one still starts from a hand-authored mask: the character editor paints one, but the converter that used to bootstrap a character writes the retired palette schema the loader no longer accepts.
  • Portals connect two points inside one arena. Connecting separate maps into one world is designed but not built.

0.6

the arena grows depth, and the people in it start talking.

0.5 filled the room. 0.6 builds the room around them. An arena is lit rather than uniformly bright, so a corridor can be dark and the hall at the end of it warm, and a lamp you shoot out takes its corner with it. Walls can be seen through, shot out, opened, and walked straight through into somewhere else. The shopfronts have rooms behind them. Six arenas join the rotation, capture the flag joins the modes, the flamethrower joins the arsenal, and every soldier in the match has a voice.

Soldiers speak

Your soldier has a voice. Five personalities ship, each with its own catchphrases: SARGE, GHOST, ROOKIE, WRENCH and CHAPLAIN. Pick one on the VOICE row of the CHARACTER screen, where Enter auditions it, and it travels with the rest of your look, so everyone in the room hears you as the soldier you built. Bots draw a voice from their slot, so a solo match hears all five.

Lines come out of the match itself. A soldier calls the frag, answers a death, takes the lead, wins the match, and speaks on every flag beat in capture the flag. One voice sounds at a time and one line covers one moment, so a firefight stays legible rather than turning into five soldiers talking over each other, and a speaker waits before speaking again.

Hold V for the comms wheel and pick a line to say it on purpose: NEED BACKUP, ENEMY SPOTTED, COVER ME, ON MY WAY, or a TAUNT. The four callouts are team coms and carry to your teammates flat and at full level wherever they are; the taunt is heard by everyone in earshot, from where you stand. Touch players get the same five on a COMS button. Speech rides its own volume slider in OPTIONS, next to music and effects.

Protocol 17. A soldier's personality travels with the appearance and a spoken line is its own action on the wire, so this build and a protocol 16 one land in separate rooms.

Capture the flag

A third mode joins deathmatch and gun game. Two flags, one per side, each on a base its team spawns around: take theirs, carry it home, and score it against your own flag standing. A carrier who dies drops the flag where they fell, either side can reach it, and a dropped flag walks itself home on a timer if nobody does. A match clock runs the mode to a finish, and a tie at the end is a draw rather than a winner picked on a technicality.

The flags are readable from anywhere. They stand in the world and show on the minimap, every take, drop, return and capture is announced and carries its own cue, and a flag that is out of place is marked so you can see at a glance which half of the fight you are losing. Bots play the mode properly: they take the enemy flag, run it home, and turn back to defend their own when it goes.

Two arenas ship with it. DEPOT is open ground between two sheds, a long run with nothing to hide behind, and BAZAAR is covered lanes with no clear shot down any of them. A map declares its bases and team spawns in its own legend, and the menu offers only the arenas that have them when the mode needs them.

The flamethrower

A sixth weapon takes the last slot. It fires a short, wide cone that sets what it touches alight, and the burn goes on damaging its victim after the flame stops: a target that walks out of range still has a few seconds to think about. Fuel is its own ammo, dropping in its own boxes on the same pads as the rest. The plume is drawn in the world rather than only on the shooter's screen, so a flamethrower down a corridor is a thing everyone in the arena sees, and a burning body burns for everybody.

Light

An arena carries its own lighting. A map declares its ambient level and its floor and ceiling materials, floor and ceiling are textured and lit the way the walls are, and a per-cell lightmap is baked when the map loads, so a corridor can sit dark while the room at the end of it is warm. Props emit light: ceiling fixtures, cage lamps and neon signs light the rooms they hang in, and shooting one out drops that room to the arena's ambient level and relights everything around it. Shattering a pane relights too.

An explosion lights the room it goes off in. The blast throws a red glow across the walls and floor around it for as long as the fireball lasts, then the room settles back to how it was lit before, and the bloom on the screen matches the light it is throwing.

Glass, and what you can see through

A wall can be see-through, and some of them break. A material says for itself whether it is opaque, so the raycaster walks past a railing or a run of cell bars and draws what stands behind them, composited over the scene through a per-pixel depth buffer. Glass cracks through three stages before it drops, to bullets and to blasts alike, with a shatter effect and a sound of its own. Broken panes travel: a peer joining halfway through inherits every pane already cracked or gone rather than walking into a shopfront rebuilt for them alone.

Doors

A door is content now. Six types ship across three motions - the sliding door, the blast door, a roller shutter that lifts rather than slides, a glass door and two gates - each one a door.yml beside its own art, exactly as props and weapons already work. A door takes its opening direction from the map instead of putting its handle on the wrong edge of two faces out of four, a type may be see-through so a gate shows you the room before you commit to it, and every type has its own open and close sound. The map format and the map editor both carry the type, so an arena can be authored with any of them.

Portals

Two new materials cut a hole between two points in an arena. Step into one end and you come out of its paired match, facing wherever the pair points. A solid pair looks like an ordinary wall, animated and faintly shimmering, until you walk into it; an open pair is see-through, so you can look, aim and throw through it before you ever cross. Every pair works in both directions, bullets and grenades pass through an open one exactly as a player does, and bots path and aim through them like anyone else. An arena carries up to four colour-coded pairs, placed with the map editor's own Portals brush.

Threshold joins the rotation to show them off. Two private spawn rooms sit in opposite corners, linked by a solid pair for a straight shortcut through the wall between them. The second pair sits open in the arena's own outer wall, looking the length of the long central corridor.

New arenas

Six arenas join the rotation, which takes it to eleven. CITY MALL is a glass-fronted shopping centre around a planted atrium: every shopfront is a pane you can see through and then shoot out, so the sightlines change as the match goes on. POLICE STATION is darker and tighter, with a bullpen of frosted partitions, a holding-cell corridor you can see down but not shoot down, and a basement lit by four lamps that anyone can take out. CLOISTER is a ruined abbey, four halls around a fallen chapel, and it brings a ruined stone wall set with it. THRESHOLD arrives with the portals, DEPOT and BAZAAR with capture the flag.

Foliage, vending machines, filing cabinets and police barricades fill the new floor space, and the five original arenas are lit rather than left flat. A prop can hang from a ceiling and face a direction it was placed at, and the solid ones - crates, barrels, canisters - stop you walking through them instead of being scenery you pass through.

Shopfront interiors

A window on a facade shows a room behind it rather than a flat texture, with real parallax: the back wall slides against the frame as you pass, the shelving inside shifts against the back wall, and the floor and ceiling open and close as you move. Each room is lit from within rather than by the street, and breaking the glass leaves the room standing.

Seventeen kits ship, and each of the two glazed facades carries a district. The blue glass is a high street - cafe, bakery, boutique, electronics, bookshop, florist, arcade - and the green glass a back street: laundromat, pharmacy, hardware, barber, noodle bar, pawnshop, garage, butcher. A derelict shop sits in both sets, which leaves about one shuttered unit in nine on either street. A mapper picks the district by choosing the facade they were already painting with, and a map that pins nothing at all gets the variety by itself.

Characters

POLICE OFFICER joins HERO on the shelf, so the roster is no longer one drawing and a promise. A body flinches when a shot lands on it: a character can declare a hurt pose, and the ones that ship do.

Recolor runs off a mask. A character declares its parts against a per-pixel mask carrying a part index and a blend strength, in place of colour ramps against a palette, so a tone that two garments share tints in proportion instead of being forced whole into one of them. The bake builds one lookup table per part per look, which is what makes the tool below able to preview a recolor at the speed you paint it.

The character editor

There is a character editor, shipped with the site under the Tools tab. It opens a character onto a canvas you pan and zoom, and lets its recolor mask be painted by hand: paint, erase, bucket-fill a flat garment by its sheet colour, and a strength control for the pixels that belong to two things at once. A tone strip claims a tone across the whole sheet in one action, overlay modes and coverage counts show what each part has claimed and what nobody has, and every stroke is one undo step.

The preview is the game's own bake, not an approximation of it, so what you see while painting is what the arena will draw. Parts carry editable anchors that can be measured back off the art, cells can be framed, stepped through and named, work autosaves to the browser and survives a reload, and the finished mask exports back to disk.

Weapons

Firearms throw brass. A weapon declares an ejection port and the frame its case leaves on, and the pistol, the shotgun and the chaingun each throw a spent case per shot, tumbling out of the gun in view space with tuning of their own; the shotgun throws its hull on the pump stroke rather than on the trigger.

Bots carry the whole arsenal. They pick the weapon that suits the range, swap at the same cost you pay, collect every kind of ammo box, and throw grenades whose arc they actually simulate before committing to it. Their callsigns come from an authored pool rather than a number.

Pickups

Health packs drop on the ammo pads. A pack grants 25 health and fills past 100, up to 150, so one taken at full health banks a buffer that stays until something shoots it off. The overheal shows as a row of pips above the health bar. Bots collect them too, but only walk to one when they are actually hurt.

Sound

The guns are loud now. Every def owns its clips: a weapon, a prop or an effect directory holds its sounds beside its sheet and its yml, and a clip can fire off a named animation frame or a named event rather than only at the moment of the shot. Fourteen of the thirty gameplay clips ship real recordings - the six weapons, the explosion, blood, the ricochet and the four flag cues - and the rest still play a synthesized placeholder tone.

Sound travels. A remote's weapon fire is spatialized at the peer that fired it, every remote runs its own frame clock so a peer's frame-triggered sounds play on your machine, and a thrown grenade sounds at the projectile on every client rather than at the thrower. A sustained weapon throttles its own fire clip, so the flamethrower reads as one continuous roar instead of a stutter.

Spectating

You can be in a match without being in the fight. SPECTATE on the main menu joins a room with no body: you are named in the roster, you can referee and own the bots, and you are not counted towards the players a match needs to start. The camera follows anyone in the arena, players and bots alike, draws the weapon the body you are watching is carrying, rides a dying target through its fall and dwells there before moving on, and parks itself when the arena empties. Press fire and you become a player, in place, without leaving the room.

Getting into a match

The room code comes first. Pick the room and the mode, and a match already running takes you straight in. The arena and the bots are only asked for when the room turns out to be empty and yours to found, and while you pick, the room is held for you: anyone arriving waits for your arena instead of starting their own.

The MAP screen is rebuilt around the arena rather than around a list. The map is a dim full-bleed backdrop with a crisp framed inset, its name, author and blurb in one column, and a rail that scrolls once the list outgrows the screen, with the arenas you built grouped apart from the shipped ones. The mode screen draws a mark for the mode under the caret, and ASSAULT joins SURVIVAL as a mode that is coming.

The map editor

The editor gains shapes: line, rectangle and ellipse, each with a drag preview, each writing the whole shape in one undo step. The palette shows the real tile art instead of coloured squares, the canvas paints the real wall and prop art, the selected brush is previewed, and the header operations carry icons.

New brushes cover the new content: a Portals group, team bases and team spawns for capture the flag, and the door types. An Environment section sets the arena's floor, ceiling, ambient level and interior kits, with a swatch picker for the material choices, and a map round-trips all of it, reporting the keys it does not recognize rather than dropping them silently.

The editor marks cells a player can never reach or see: a sealed room, the filler inside a thick wall, the corners of the border. They draw black with an X across them, and a toolbar toggle hides and shows them.

The HUD

The killfeed names the frag in the corner of a live match. The minimap sits in a panel of fixed size and cues its clipped edges rather than growing with the arena. Overheal shows above the health bar, portal pairs are colour-coded on the minimap, and the flags show on it in capture the flag.

The site and the tools

The credits roll names the arenas. Every shipped arena appears with the person who built it, read from the map itself, so a new arena credits its author the moment it ships. The special thanks list moves into a file of its own and the attributions page is generated from it, so adding a name never means editing the credits.

The landing page's arena board is boxed with its columns aligned, the live arena widget becomes a card, and a room with a match actually running is marked with a pulsing dot. There is a Character Editor card on the Tools tab beside the map editor.

The renderer can photograph itself. A console command captures every draw stage of a frame separately - walls, sprites, decals, the viewmodel - and a headless script poses a reproducible scene and shoots the same set from the command line, which is where the frame breakdowns on the site come from.

Networking

Protocol goes from 9 to 17. Eight steps, each one closing a way two clients could silently disagree rather than error: v10 and v11 add a voice and take it back out again, v12 and v13 put breakable glass on the wire and add the founding claim that stops two clients building different arenas in the same empty room, v14 adds the flamethrower's slot, its fuel box and its burn, v15 adds the health pack as a sixth drop kind, v16 puts capture the flag's flags, captures and match clock in the referee's snapshot, and v17 gives a soldier a voice again, this time from files. A 0.5 client and a 0.6 client never meet.

The in-arena spectator is a third role on the mesh alongside player and observer. TURN servers join the signaling config, so two peers behind restrictive networks can actually connect rather than sitting in a room together and never finding each other. A net console command opens a live traffic panel with per-peer stats and relay counts. An operator can run an always-on host with the container and compose file that ship in host/: a headless spectator that holds a room open so a match is there to join.

The boot intro

The boot screen is an intro now, not a two-second loading gag. It opens on a POWER plate and waits there: a browser will not play sound until you have told it to, and this one carries sound, so it asks first, with a press of any key, a click or a tap. That wakes a CRT - a line blooms open, overshoots, and settles into the amber field as the header fades up - and then runs the same three loading stages as before, LOADING ART, BAKING SPRITES and LINKING PEER MESH, paced to be watched this time instead of blinked past: each one clatters while it works and reports OK or FAIL when it lands. The lines fade, the logo scans in a row at a time the way a tube draws a frame, and a half-second fade hands off to the menu, its music starting as the CRT hum dies out under it. About 4.7 seconds end to end on a warm cache.

Any key, click or tap during the show skips it, but never the loading underneath: every stage is still awaited, because the menu cannot open before the art it draws with has loaded. A power-on thunk, a hum bed, drive chatter under each stage, and a swell on the logo all currently play as synthesized placeholders, briefed in PENDING_AUDIO_ASSETS.md alongside the rest.

Known limitations

  • Twenty of the thirty-four gameplay clips are still a synthesized placeholder tone: the six door pairs, the fire and glass effects, the prop break, the portal traversal, and the four boot intro sounds. PENDING_AUDIO_ASSETS.md carries a brief for each.
  • Only the default sliding door is placed in the shipped arenas. The other five types are authorable in the editor and the map format but appear in no map.
  • Survival and assault are both in the mode list and both locked.
  • Two characters. HERO and POLICE OFFICER are the shelf, and a new one still starts from a hand-authored mask: the character editor paints one, but the converter that used to bootstrap a character writes the retired palette schema the loader no longer accepts.
  • Portals connect two points inside one arena. Connecting separate maps into one world is designed but not built.

0.5

the room stops being empty.

0.4 ended on an admission: still no bots, an empty room is an empty room. 0.5 answers it. There are people in the arena now, they path and hunt and take cover, and they are drawn rather than computed - eight facings, a real walk, a death that plays out. Around them the game grows the parts that were missing: a front door you arrive through, an editor for building your own arena, a jukebox for the soundtrack, and a console for prying the whole thing open.

Bots

You can fill a match with bots, up to seven, chosen on a BOTS screen that follows map selection. They are not target dummies. A bot paths across the arena with A* over the tile grid, decides between hunting, rearming, retreating and wandering, and shoots at you with an aim error and a reaction delay rather than with perfect instant fire. It rearms at ammo pads and backs off while it does.

Four levels set how well that goes: BYSTANDER, EASY, NORMAL and HARD. Skill moves aim error, reaction time and sight range together, so a HARD bot sees further, turns on you sooner and misses less. BYSTANDER is the odd one out - it cuts combat entirely and only roams, which makes it useful for walking an arena you are building, or for filling a room with something to watch.

Bots live on the wire, not in one client. A single peer simulates each bot and broadcasts its position, hits and deaths, and if that peer leaves, another adopts the bot and carries it on. Everyone in the room sees the same bots doing the same things, and they score on the same board you do.

Characters

The figure you play is a character now, and the first one is HERO. A character is one spritesheet plus a small file beside it that says where each pose sits on the sheet and which of its colours are skin, hair, shirt, trousers and boots. The PLAYER screen gains a CHARACTER row that names the one you are wearing, so a second character is a folder of art rather than a code change.

All eight directions are drawn. The old sprite painted five and mirrored the other three, which meant a soldier seen from their left was the same soldier seen from their right, flipped. Every facing is its own picture now, so a player turning in front of you turns through eight drawings instead of five and their reflections.

The way people move

Walking is four frames instead of two, and there is a separate standing pose, so a player who has stopped stands still instead of marching on the spot.

Dying is a sequence rather than a swap. Seven frames carry a body from the hit through the stagger to the floor, it plays once, and it holds where it lands instead of snapping to a corpse. A body already on the ground when you arrive stays down rather than dying again for your benefit.

A peer shooting at you head on looks like it. Two more poses cover the moment of a shot - the gun raised, then the muzzle flash - and they replace the walk while the shot is fresh, so the player in front of you visibly fires the round that hits you. They are drawn front on, because that is the view that matters.

Customization

Five colour rows dress a character: SKIN, HAIR, SHIRT, TROUSERS and BOOTS. Skin, hair and boots each get their own palette of tones that suit them, rather than sharing the arena's sixteen. The preview turns with A and D so you can check a look from every side before you take it into a match.

Glasses and hats are gone. They were switches on a painted sprite and there is nothing to switch on a drawing; what replaces them is art, and a character that wants a hat wears one its artist drew.

Weapons

A weapon is a folder now. Each of the five - knife, pistol, shotgun, chaingun, grenade - is a weapon.yml beside a spritesheet, and the game fills its weapon table from that library instead of from code. The sheet carries everything the weapon shows: the first-person viewmodel, the projectile it puts in the world, and the debris its blast throws.

Switching is animated. The old gun drops out of frame, the new one rises, and the trigger is locked while the swap plays, so a switch costs you the time it looks like it costs. Firing animates too, with fire and recover frames, and the grenade pulls its pin before it goes.

The spread weapons spread properly. The chaingun sprays into a cone rather than down a line, and shotgun pellets scatter on both axes instead of only sideways. A grenade's throw distance follows how long you hold the trigger, so a lob and a hard throw are different shots.

Vertical aim

Looking up and down changes what you hit. In 0.4 the vertical axis moved the view and nothing else: a shot went where your feet pointed no matter where your eyes were. Pitch decides the shot now. Aiming high hits what is high, aiming at the floor hits the floor, and the impact stamps its decal at the height the shot actually struck rather than at chest height.

Props

The props become a library on disk. Each is a folder holding a prop.yml and a sheet that describe its size, its behaviour, what it does when it breaks and what it throws, and a migration tool generated the set from the twelve the game already had. Thirteen ship now: fire is new, an animated idle that hurts anything standing in it through a proximity aura that ticks damage while the prop is intact. A prop can also be consumed by its own destruction.

Shots respect a prop's shape. A bullet crossing a transparent texel of a sheet no longer counts as a hit, so a shot threads the gap in a railing instead of stopping at its bounding box. Maps validate their legends against the loaded library, so a map naming a prop that does not exist fails loudly instead of quietly drawing nothing.

Debris

A prop's wreckage used to look the same no matter what killed it: a barrel knifed open flew apart exactly as hard as one caught in a grenade, because a single speed and spread served every weapon. Debris now takes its cue from what broke it. A knife crumbles a barrel at its base, a bullet knocks its pieces a little wider, and a blast throws them clear across the room.

Authors can tune each case per prop. A prop's destruction.particles in its prop.yml gains optional melee, hitscan and blast blocks that override count, size, speed, up, ttl or tint for that cause alone. Anything a block leaves out falls back to the prop's shared particle values, and then to the engine's own default for that cause. No shipped prop declares a block yet - the defaults already carry the whole effect.

Death by explosion is messier than death by bullet. A body caught in a blast bursts into giblets instead of falling into the dead pose, thrown by the same physics pool that carries prop fragments, and the pieces fade out rather than blinking away. Bots and corpses already on the floor go the same way. The giblets are hand-drawn on a grid sheet now rather than painted at boot, and a GIBS flag under a new GAMES settings category turns the whole thing off.

Blood and explosions

Effects come off spritesheets rather than out of code. art/vfx/ holds an explosion and a blood burst, each a sheet plus an fx.yml saying how it is sliced and timed, and the procedural painters that used to draw them are retired. A sheet can declare an integer scale, so a cell smaller than 64x64 still draws at the size the effect wants.

An explosion scorches what is near it. A blast scans the wall faces around it and stamps deterministic scorch decals, so the burn marks fall the same way rather than landing at random.

Maps

Two city arenas join the three industrial ones: HIGH STREET, a shopfront avenue, and PLAZA, a civic square. They bring an urban wall palette - glass, stucco, shutter, awning, neon and sandstone - and the street furniture to dress it: hydrants, planters, mailboxes, lightpoles and benches.

Every map carries an author, credited on the map screen. Legends are per map now: the digit binding that made every arena share the same nine materials is retired, and a map says for itself which tile means what.

The map editor

There is a map editor, shipped with the site under a Tools tab. It is a canvas you pan and zoom, with the palette grouped into tiles, walls and props, undo and redo, resize, in-page dialogs in place of browser prompts, and a guard so you cannot walk away from unsaved work. Maps you build are kept in your browser and appear in the menu beside the shipped ones.

Maps travel. A peer who does not have the arena you picked asks the peer that named it for the body and gets it over the wire. Received maps sit beside your own, marked as shared, and are capped so a stranger cannot fill your storage.

Music and the jukebox

Music is a registry. music.yml keys every track by id and the game asks for tracks by name rather than by path, so a map naming a track it does not have falls back to a default arena song instead of playing nothing.

There is a JUKEBOX screen. It lists the soundtrack with genre and lyrics chips and cover art, plays, seeks, shuffles and repeats, and remembers all of it between sessions. Behind it sits a winamp-style player window with a spectrum analyzer driven by an FFT tap on the music bus.

The music follows you out of the menu. L raises a small music widget on any screen and in-match, fading in and out rather than snapping, and a NOW PLAYING toast names the arena track when a match starts.

Menus and options

OPTIONS splits into GRAPHICS, AUDIO and CONTROLS. Controls gains a scheme selector that resolves against your device, and the in-match keys come from the scheme you picked rather than from one hardcoded set. Graphics gains an FOV slider, a SHOW FPS readout, a fisheye lens toggle, and the CRT interlace overlay as an option that applies in the match too.

The SOLDIER root item is now PLAYER, and there is an EXIT item in the main menu and the pause overlay. You run with shift, and an AUTORUN row inverts it so you run by default and hold shift to walk. Respawning waits for you: instead of snapping you back, the game prompts PRESS FIRE TO RESPAWN and holds until you do.

Screens dissolve into each other. A transition plays between screens and into the match - melt by default, with fade, wipe and cut available and per-route overrides declared in game.yml.

Touch

The game plays on a phone. An on-screen overlay carries movement, look and fire, with a look-sensitivity control in options; a menu pad lets a touch player drive the menus and get back out of them; and the room code is edited with the device's own keyboard.

The developer console

~ drops a console into a match. It has a command registry, a parser, editable input with history, and a scrollback you can walk with alt+up/down. The commands cover inspection (pos, peers, fps, and debug for a render-stats overlay), display (nametags, minimap, fisheye, gibs, interlace, fov, sens) and cheats (god, noclip, infammo, give, bot). Every hook defaults to the behaviour the game already had, so a console you never open changes nothing.

The site

Fragyard has a front page. The root serves a landing page and the game moves to /play/, with an arena card carrying the Play button and tabs for About, Roadmap and Tools. The live arena widget watches real matches: it lists rooms, names the top three fraggers with their kill counts, and narrates frags as they land with a typewriter reveal. These release notes are generated from RELEASE_NOTES.md and served at /release-notes.

The game has a logo: a reticle-skull lockup that also supplies the favicon and the link preview image.

Networking

Protocol goes from 6 to 9. Three steps, all of them additive to the room but none of them backward compatible: v7 carries bot spawn, position, hit and death; v8 adds an observer role with filtered rosters, so someone watching a match is not counted as a combatant; v9 adds map request and reply so a peer can fetch an arena it does not have. A 0.4 client and a 0.5 client no longer meet.

An invite URL auto-joins the match it names after boot instead of dropping you at the menu. Sitting in the menu counts as watching, through a quiet lobby room, so the site can tell how many people are around without pretending they are playing. The signaling relays move into game.yml, with damus swapped out for primal and nostr.band.

Known limitations

  • Sound is still thin. Music plays and the menu moves and confirms, but the guns, the explosions and the footsteps are silent, even though the spatial engine to place them has been in since 0.4.
  • Decals, gibs and debris are local flourishes. Two players do not see the same scorch marks or the same giblets.
  • One character. HERO is the only one on the shelf; a second is a folder of art nobody has drawn yet.
  • Survival is still listed in the menu but locked.

0.4

the game gets a voice, and everything you see becomes a file you can edit.

0.3 turned the arena into data. 0.4 does the same to the rest. The art loads from PNGs you can open in any editor, the title and credits come from a config file, and the whole thing finally makes sound. The soldier is repainted at four times the resolution, walls come in fourteen materials and remember where they were shot, and you can look up and down and fall over when you die.

Sound

Fragyard has audio now. A small Web Audio engine sits behind two buses - music and SFX - each with its own volume, set on a new OPTIONS screen and remembered between sessions. Music streams rather than decoding whole: a menu track, a credits track, a victory sting when a match ends, and a per-map arena track that each map declares in its own yml. A missing track is silence, not an error, and a same-map rematch restarts the arena track cleanly rather than layering a second copy over it.

The context stays asleep until your first keypress on the boot screen, because a browser will not let a page make noise before you have touched it. Sound effects share the same plumbing - including a spatial layer that pans and attenuates by position - but only the menu speaks so far: moving the cursor and confirming a choice each have a sound. The guns are still silent (see Known limitations).

Art you can edit

Static art - wall textures, props, the HUD, the soldier - is no longer painted in code at boot. It bakes into PNG files that ship in art/, and the game loads them at boot instead. An export tool regenerates the whole set from the painters, so the pixels are still procedural at the source, but what the game reads is a folder of images you can open in any editor and change by hand. Soldiers bake from separate body, hat, and glasses layers rather than one flat sheet.

One config file

Title, version, and the credits roll now live in game.yml, read by both the boot screen and the menu, and the old credits.yml is gone. A CREDITS entry in the menu plays a scrolling roll built from that config. Credits text is limited to the pixel font's charset, so the file documents which characters render and which come out as gaps.

The soldier, repainted

The SOLDIER screen moves onto the canvas and drops its old DOM island, so the whole shell is now drawn the same way. The sprite pipeline moves to 128px and the soldier is repainted in detail, with shaded garments in place of flat fills. A 16-color palette picker with a typed hex field sets each part, and a live preview shows the result.

Walls

Wall textures are a registry now: fourteen materials painted with a shared toolkit, with the minimap and thumbnail colors routed through the same registry so an arena reads consistently at every scale. A map's grid can reference any of the first nine materials by digit.

Walls also remember where they were shot. Every hit stamps a decal - a blood splash or a ricochet scorch - onto the exact wall face it lands on, resolved to the nearest solid surface and stored sparsely per face with an LRU cap so memory stays bounded. Impact bursts become world-anchored billboards that sit in the scene and animate in place rather than flashing at the screen center.

Looking and dying

Mouse look gains a vertical axis: you can look up and down within a clamp, and strafe under the pointer lock. Death is staged now - the camera drops to the floor, the world rolls as the body goes down, and the fall plays out before the spectator camera takes over.

Controls and UI

The mouse wheel cycles weapons, skipping the ones you cannot hold. The Tab scoreboard renders on the canvas in the pixel font. A new OPTIONS screen holds the volume sliders and a display mode that fills the window by default, replacing the old corner fullscreen button.

Gun Game

The ladder reverses. Gun Game used to hand you the knife first, the hardest rung, so early kills went to whoever walked into a body. It now starts you at the grenade and demotes toward the knife, so the lead narrows as it grows and the last kill is the hardest one.

Networking

Protocol stays at 6. Nothing this release touches the wire: sound, loadable art, wall decals, and the death fall are all local. A 0.3 client and a 0.4 client still meet in the same room and agree about the world - they just hear and see it differently.

Known limitations

  • Only music and menu navigation make sound. Gunfire, explosions, and footsteps are still silent, even though the spatial engine to place them is already in.
  • Wall decals and the death fall are local flourishes. Two players do not see the same scorch marks, and no one else watches you fall.
  • Still no bots. An empty room is an empty room.
  • Survival is still listed in the menu but locked.

0.3

the arena stops being a constant, and the published site stops being the source tree.

0.2 shipped four modes and something to blow up, all in the one U-shaped arena that had been baked into the code since 0.1. 0.3 turns the arena into data: maps load from files, three of them ship, and you pick one before the fight. The site served from the web also changes shape - it is now a built artifact rather than the raw source folder.

Maps

The arena is a file now. An arena is one ASCII picture in maps/<id>.yml, listed in maps.yml: digits are wall textures, D is a door, S a spawn, A an ammo pad, and single letters place barrels, gas canisters, crates, pillars and lamps. You author a map by drawing it, and a test parses every shipped map and rejects a grid that is not rectangular, not walled all the way round, or missing a spawn or a pad - so a broken arena fails the suite instead of someone's browser.

Three ship:

  • Foundry - the original 0.1/0.2 arena, ported faithfully to a file. Same two rooms and the killing floor between them.
  • Crucible - tight, tall, and close: a spine of doored chambers built for the shotgun and the knife.
  • Yard - open ground and long sightlines, with just enough pillar cover to break them. Territory for the chaingun.

A map-select screen sits between the mode and the room code, with a top-down preview of the highlighted arena and a RANDOM entry that rolls a real map before the match starts, so the share link always names the arena you actually got.

How the map is chosen

Unlike the mode, the map is not part of the room's identity. The Trystero room is still <mode>:<code>, and the arena is whatever its founder picked: whoever reaches an empty room first sets the map, and everyone who joins after inherits it. This is the only honest way to offer "pick a map if the room does not exist yet", because a peer-to-peer mesh cannot be asked whether a room exists without joining it.

So a join now holds on an ENTERING ARENA screen for a beat while it learns the arena - from a peer's catch-up in the common case, or by founding its own pick if the room really is empty. A share link carries the map as a hint (#room=TEST&mode=dm&map=yard): it reproduces the arena for a friend who arrives first, and is quietly overridden if the fight is already running on a different one.

Published builds

The site served from fragyard.4rknova.com is no longer the source tree. It is a bundled, minified, lightly obfuscated dist/, built and deployed by CI. Around 180KB of hand-written modules collapses to one hashed bundle, roughly 20KB over the wire, and the raycaster, the procedural art, and every comment in them are no longer served raw to anyone who opens the network tab. This is not anti-cheat - the game is peer to peer with a trusted referee, and a determined cheat sends bad packets rather than reading code - it just deters casual tinkering and makes a published build look published.

The repo stays a plain folder of ES modules that runs under python3 -m http.server with no build step. Building is a publishing step, never a development one.

Networking

Protocol 6. The map now travels on the wire - it rides the referee's 2 Hz snapshot and the join catch-up - and a prop id is an index into the chosen map's props rather than one fixed array. A 0.2 client carries no map field and would play its own hardcoded arena while everyone else played the loaded one, so the protocol version steps up and keeps the two apart: mismatched clients land in different rooms and never meet, the same guard that has kept every silent-desync change honest since 0.2.

Known limitations

  • Still no bots. An empty room is an empty room.
  • Survival is still listed in the menu but locked.
  • Maps ship with the build. There is no in-game editor and no way to load an arena that is not in the manifest.

0.2

the game gets a front end, a rule set, and something to blow up.

0.1 is a deathmatch that never ends, in an arena you cannot damage, on a page that drops you straight into it. 0.2 adds a boot screen, a menu, four playable modes with real win conditions, explosives, and props that come apart when you shoot them.

Game modes

Pick one in the menu. The mode is part of the room's identity - the Trystero room is <mode>:<code> - so two players who choose different rules land in different rooms and never meet. Share links carry both: #room=TEST&mode=tdm.

  • Deathmatch - free-for-all, first to 20 frags.
  • Team Deathmatch - red against blue, first team to 40. Teammates cannot hurt each other, and nametags and the scoreboard switch to team colors so sides read at a glance.
  • Gun Game - every frag promotes you one rung up the ladder (knife, pistol, shotgun, chaingun, grenade); finish it to win. Your rung is your kill count, so the mode needs no state of its own. Ammo boxes are switched off, since the rung weapon never runs dry.
  • Last Man Standing - one life a round, last alive takes it, first to 3 rounds. Eliminated players get a spectator camera: left and right cycle through the survivors, and you watch through their eyes until the round ends.
  • Survival - co-op waves. Listed but locked; it has its own spec and is not built yet.

Matches now have a shape: warmup, live, round over, match over, rematch. A results board closes each match, and the arena resets between them, so props you destroy come back and the third rematch is not played in a burnt-out room.

Match state and the referee

Phase, clock, scores, and teams belong to a referee: the peer whose Trystero id sorts lowest. Nothing is elected - every client derives the same answer from the peer list, so the role migrates for free when the referee leaves. The referee broadcasts a snapshot at 2 Hz and everyone else adopts it, ticking the clock down locally in between. The referee is trusted; with no accounts and no ranking, that is the right trade.

Weapons

Keys 1-5 select: knife, pistol, shotgun, chaingun, grenade. Each gun draws from its own reserve, so running one dry does not touch the others.

  • Shotgun - eight pellets in a cone, damage falling off with range.
  • Grenade - thrown on an arc, bounces off walls and closed doors, detonates on a two-second fuse. The blast falls off with distance, is blocked by walls and closed doors (open ones let it through), and does not care that you threw it: you can absolutely kill yourself with it.

Props

The arena is destructible now. Explosive barrels detonate when shot or caught in a blast, and chain into each other. Gas canisters vent and skitter across the floor before going off. Wooden crates splinter. Stone pillars are indestructible cover: they block movement and eat shots.

Boot screen and menu

The page opens on a DOS-style boot sequence, a BIOS gag whose progress lines are honest. Each stage awaits a real dynamic import: the texture bake, the sprite bake, and a prefetch of Trystero. That last one is the substantive one - it warms the module cache so the first connect of a match resolves instantly instead of stalling. A stage that fails prints FAIL and carries on rather than trapping you.

Boot hands off to a canvas-drawn retro menu that owns the screen until a match starts and takes it back when one ends: mode select with per-mode blurbs, the arena and room code, controls, and the soldier screen (the old 0.1 lobby, which no longer decides anything on its own). Esc during a match opens a pause overlay. It is an overlay, not a pause, because a multiplayer arena cannot stop for one player. Leaving tears the room down and returns you to the menu.

Under the hood

  • main.js splits up: procedural art into art.js, particles into fx.js, the pixel font into text.js, combat math into combat.js, plus the new modes.js, match.js, menu.js, boot.js, and spectate.js.
  • Every module that can be tested without a canvas now is: the combat math, the mode rules, the phase machine, the pixel font, the spectator index, and the menu's share-link arithmetic. Node's built-in runner, no dependencies: node --test tests/*.test.js.
  • The renderer draws through a camera that normally aliases the player. That indirection is what lets an eliminated player be repointed at a teammate's eyes without gameplay noticing.

Networking

Protocol 5. The Trystero room is namespaced by protocol version, so incompatible clients land in different rooms and never discover each other. This matters because prop ids and the ammo-drop schedule are derived on every client from a shared seed rather than broadcast: a mismatched client would silently disagree about the world instead of erroring. A version mismatch now looks like "waiting for players", not a desynced arena.

Door state, prop destruction, and canister behavior all sync now - three places where two clients could quietly resolve the same blast against different worlds and never reconcile.

Known limitations

  • Still one map.
  • Still no bots. An empty room is an empty room, and Last Man Standing needs two players before its warmup countdown will even start.
  • Survival is listed in the menu but locked.
  • Die late in a Last Man Standing match and you stay dead under the results board, which covers the screen anyway.

0.1

first tagged release.

A browser deathmatch in a Wolfenstein 3D-style raycaster. Customize a soldier, join an arena, and frag your friends. Everything renders to a 2D canvas with classic DDA raycasting, and the multiplayer runs peer-to-peer with no backend to deploy.

Gameplay

  • Three weapons, each on its own ammo reserve: the knife (free forever, no reserve), the pistol, and the chaingun. Picking a gun you have no ammo for is refused rather than silently allowed - the status bar blinks instead - and firing a gun dry drops you back to the knife.
  • Ammo economy. You spawn with 24 pistol rounds and 60 chaingun rounds, capped at 99 and 200. Boxes on the floor grant 16 and 50 respectively, and a box you cannot use stays standing rather than being wasted. Pickups are claim-raced between peers, so two players grabbing the same box cannot both bank it.
  • Death is never a dead end. Dying costs you two seconds and respawns you with a fresh loadout, away from the other players when the arena has room.
  • Frags and deaths are tracked per player, with a hold-to-view scoreboard listing every peer's kills, deaths, and live ping, sorted by kills.

Multiplayer

Networking is serverless. Peers find each other over Trystero - WebRTC signalled through a pinned set of public Nostr relays - so there is no backend, no account, and nothing to host. Each client owns its own avatar and broadcasts its state; hits are routed directly to the peer that was hit.

  • Join the public arena or type a room code to play with a specific group.
  • Remote players are interpolated between updates and drawn with nametags, so motion stays smooth across a lossy mesh.
  • Peer names are sanitized before they are rendered.

Secure-context requirement. WebRTC signalling needs crypto.subtle, which browsers only expose on secure origins. Fragyard works over HTTPS and on http://localhost; on a plain-HTTP LAN address it degrades to solo play and says so plainly instead of failing silently.

Look and feel

  • Customizable soldiers. Shirt color, trousers color, hat, hat color, and glasses are all set in the lobby, and the sprite is generated procedurally from those choices, so there are no avatar spritesheets to ship.
  • Eight-directional sprites. Remote players are drawn with the frame that matches their facing relative to your view, so you can read which way an opponent is turned.
  • Hit effects. Blood and ricochets are synced across peers, scaled by distance, and dimmed by the fog so they sit correctly in the scene.
  • Pixel status bar in the classic idiom, and an aspect-preserving fullscreen toggle that letterboxes rather than stretching.

Known limitations

  • One map. The arena is a single U-shaped wood corridor.
  • No bots. An empty room is an empty room.
  • No match structure: no timer, no frag limit, no win condition. You play until you stop.