Devlog ·

v0.0.2: the void, drawn

Cog Wars v0.0.2 gets a living fractal void, sacred geometry, neon halos and particles. What changed, what broke on the way, and what is still missing.

Every screenshot of Cog Wars before today had a black background. The v0.0.1 post said so plainly: no particles, no post-processing, none of the sacred geometry, “a clean, readable, deliberately plain version of a game that intends to look like something else entirely.”

v0.0.2 is the something else. The build at /play/ now draws a living fractal void behind the arena, the Flower of Life woven into that background, a neon halo on every enemy, a field of drifting particles, and a post-processing chain over all of it. All of it answers one number that you control by how well you are playing.

Wave ten in Crystal-Void: a knot of Drones and Seekers glowing orange and red over a teal and violet fractal void, with the Flower of Life's circles crossing the arena.

Wave 10, in the v0.0.2 build. Staged: the wave was skipped to and fast-forwarded, and intensity pinned at 0.5, which is about as high as a good combo takes it in this version. How every screenshot here was made is at the end.

The same second, in two versions

The fastest way to see what changed is to play the same moment twice. Both builds below were started on the same seed and photographed about 3.6 seconds into a run: the same Drone coming in from the right, the same two weapon pickups.

Cog Wars v0.0.1: the player, one Drone, two weapon pickups and magenta projectiles on a flat black background with a faint grid.

v0.0.1, the shipped build, unassisted.

Cog Wars v0.0.2 at the same moment: the same player, Drone and pickups, now over a teal and violet fractal void, with the Flower of Life's circles and an orange halo around the Drone.

v0.0.2, the shipped build, unassisted. The fight is the same; the frame is not.

Nothing about how the game plays changed between those frames. The difference is five new layers, and each one is worth a paragraph.

The void. Behind the arena is a fractal field that breathes, drifts and zooms. It is one hand-written shader evaluating a noise field, and its colour travels across exactly the part of the colour wheel that Crystal-Void’s own palette covers: 97 degrees, from cyan at 185° to purple at 282°. It never crosses into the greens, ambers and reds the universe does not use, because a universe’s colours are part of its identity and the other four universes will need theirs.

The Flower of Life. Nineteen circles woven into the void, always faintly present. The figure is sized from the arena, not typed in: its outer edge is three times the radius of one circle, so a radius of 5.4 world units inscribes the whole figure in the arena exactly. A circle that would pass within 3 units of you is removed before it is ever drawn, which is why there is always a clean hole around the player.

Neon halos. Every enemy glows its own colour. The halo’s edge is built from three ripples moving at speeds that never line up, so it wobbles and never quite repeats. Round those speeds off to something tidy and the glow settles into a regular shape, which is exactly what the design rules out.

Particles. Motes drift in a ring around you, with a hole exactly the size of your legibility radius. Particles near the player are not thinned out; they are impossible, because the emitter cannot place one there. Kills throw a small burst.

The post chain. Bloom on the bright edges, a chromatic-aberration pulse, a slow drift of the whole frame’s hue, and a vignette. The chromatic cap is the one level a human has actually signed off: three captures at 0.45, 0.20 and 0.08, compared side by side. At 0.45 every arc of the geometry doubled into a red and cyan ghost and read as smeared; 0.20 won.

One number runs the whole screen

The layers above do not each decide how loud to be. They all read a single value called intensity, and intensity comes from you:

  1. Kill something and it drops fragments (the small gold cogs).
  2. Collecting fragments steps your multiplier up.
  3. Every multiplier step adds a little intensity, about 0.02.

As it climbs, the void zooms deeper (geometrically, about 1.1 octaves for every tenth of intensity, toward a ceiling of roughly 2,184× magnification), the particle field thickens and the chromatic pulse opens up. The sacred geometry answers too, but only near the top of the range; more on that below. A careful, quiet start looks calm. A good combo makes the screen come alive. That is deliberate: the game should look the way the run feels.

The player surrounded by gold fragments just after a group of enemies died; the multiplier reads x2 and the score 1000.

Fragments scattered around the player after a group of enemies die at once. Picking them up is what drives intensity. Staged with the developer console’s kill command.

Practical tip: you collect a fragment by flying over it, so a kill on the far side of the arena only feeds your multiplier if you go and get what it dropped. Fighting close and sweeping through your own kills is how the screen lights up.

The ceiling you cannot reach yet

The sacred geometry is built to escalate. At rest the Flower of Life sits at a faint floor. Higher up the intensity range it brightens, and a second figure surfaces behind it: Metatron’s Cube, 13 centres joined by 78 chords, drawn as fine lines and carved out around the player like the circles are. Here it is at full intensity:

The arena at intensity 1.0: the Flower of Life's circles now crossed by the fine straight chords of Metatron's Cube, with a clear space around the player.

Intensity pinned at 1.0 with the developer console. You cannot get here in v0.0.2.

You will not see that in this version, and we measured why before writing this. A combo alone lifts intensity to about 0.5. With intensity pinned at exactly 0.5 in this build, the geometry reports strength=0.25 cubeStrength=0: the Flower is still at its floor and the cube has not started. At a pinned 1.0 it reads strength=0.379 cubeStrength=0.173. The other two sources of intensity, deploying a cog and a boss changing phase, are wired but still contribute zero, and those are what will carry a run past halfway. Both figures are built and waiting. This is listed under Known gaps below rather than quietly left in the screenshots.

A field guide to the halos

Halo colour is per enemy type, so once you know the five colours you can read a crowd at a glance, even when the sprites overlap.

All five enemy types around the player: a cyan-glowing jellyfish Drifter, a red Seeker on the player, an amber-glowing crystal Splitter, an orange Drone and a violet-glowing Orbiter ring.

The whole roster at once, spawned with the developer console: Drifter (cyan, left), Seeker (red, already on the player), Splitter (amber), Drone (orange) and Orbiter (violet, bottom right).

Enemy Halo How it moves Hits* Frags
Drone orange Straight at you 4 2
Drifter cyan Wanders and ignores you 8 2
Orbiter violet Circles you at a distance 6 2
Seeker red Aims where you are going, and it is faster than you 6 3
Splitter amber Lumbers in surges 12 4

* Hits to kill with the starting Pulse Emitter, which does 9 damage per projectile and fires three per pull. Frags = fragments dropped. Figures are read from the game’s own data files for this build.

How to use it:

  • Red first. The Seeker moves at 11 units a second against your 8, and it leads its target, so running in a straight line is exactly what it is built to punish. Kill it early or dash through it.
  • Orange is a crowd, not a threat. Drones are the weakest enemy, but they come in numbers. When several stack up, their halos add together into one large orange bloom, which is a useful warning that the knot is getting thick.
  • Cyan is terrain. The Drifter is not hunting you. It is a moving wall, most dangerous in the gap you were about to dash through.
  • Amber pays best. The Splitter is slow, tanky and worth four fragments. Its signature trick, dividing when it dies, is designed but not built yet; in v0.0.2 it simply dies.

Four Drones and a Seeker packed together, their orange and red halos merged into one bloom, beside a cyan-glowing Drifter.

A pack reads as one bloom. The halo’s base radius (1.6 units) is exactly the distance Drones keep from each other, so a swarm packs precisely where its glows overlap. The first capture was a single white-hot mass; each halo is now scaled to 45% so a pack stays readable. Staged.

The arena edge from v0.0.1 still kills, and you can still see it coming: the grid brightens toward the wall.

The player near the east wall firing magenta shots, with grid lines growing brighter toward the arena edge on the right.

The shipped build, unassisted: heading east, with the grid brightening toward the wall a couple of seconds before it would kill you.

Six things that broke on the way to a picture

As with v0.0.1, the features are the boring half. None of the bugs below reached a tagged build, and every one of them passed the game’s entire automated test suite, over 1,200 tests, while being completely wrong on screen. They were found by running the game and looking at it.

The shader that was not in the game. The first build with the void came back with a flat background and a console line saying the shader could not be found. The code asked for it by name, and a Unity build only includes shaders that a scene or material references. Nothing referenced this one, so it had been silently left out. The obvious fix, listing it in the project’s “always include” settings, also failed silently: the editor rewrote that settings file on the next build and dropped the entry. It now lives in a folder Unity is not allowed to strip.

The void that ate the arena. Once the shader shipped, it painted over everything: the grid, the player, the pickups, the enemy. The grid had never been assigned to any of the game’s drawing layers, so it sat on the default one, which is drawn first. Nothing had ever been drawn underneath it, so nobody could see the problem. The moment the void took the bottom layer, the whole game disappeared behind it.

The post-processing that could not happen. The renderer asset had an empty field where its post-processing data belongs, and Unity’s 2D renderer reads exactly that field to decide whether to create any post-processing at all. With it empty, bloom, aberration and vignette would have been configured, attached and ignored. This one was caught before a build, by reading the engine’s source, which saved a twenty-minute rebuild that would have come back looking unchanged.

Everything at 40%. A switch meant only for a performance-test build was, by accident, the same switch every build used, so the deployed game had been forcing its lowest quality step. That ran the whole visual layer at 35-40% of its designed strength. Before turning it up, we measured both: a steady 60 frames a second either way. (That measurement ran on a software renderer with vsync on, so it shows neither setting drops frames there and says nothing about headroom on a slow GPU. See Known gaps.)

Crystal-Void turned olive. The whole-frame hue drift was first built as a steady rotation, on the reasoning that shifting every colour together keeps them in harmony. It does keep their relationships, and at full intensity it turned Crystal-Void’s cyan and purple into olive and magenta, colours that universe does not ship. The five universes are meant to be told apart by colour. The drift now swings back and forth by at most ±12°, a bound taken from the narrowest colour band in the palettes, so it can never erase a distinction a palette draws on purpose.

The particles that stopped at 70%. The particle field read the same at intensity 0.7 as at 1.0, so the top of the range did nothing. The legibility rule that thins particles near the player had been handed the player’s distance from the middle of the arena rather than each particle’s distance from the player, so it clamped the whole field whenever you stood near the centre, which is most of a run. Making the emitter a ring around the player fixed the rule by geometry and freed the range:

Intensity Particle density before After
0.35 0.23 0.23
0.70 0.35 0.46
1.00 0.35 0.65

And a bonus seventh, because it came from a playtest rather than a test: “after I die I can’t click anywhere, such as New Game, but I can see the effects on screen still moving.” That second clause was the whole diagnosis. Two of the new visual layers read the player’s position every step to keep the geometry and particles off you, and after death there is no player, so reading it threw an error. The game’s fixed clock calls its listeners one after another, and an error in one silently stops every listener after it, including the one that restarts the run. The void is called earlier in that chain, so it kept animating behind a dead button. Now “there is no player” is a case each layer answers: the particles stay where they are, and the geometry keeps every circle because there is nobody to keep it off. That is why the Flower is complete on the death screen:

The death screen in v0.0.2: the full Flower of Life with no gap, because there is no player to carve around, and the YOU DIED / NEW GAME (R) prompt.

Between runs, the shipped build, unassisted. Before this fix, it produced 60 errors in 30 seconds and a NEW GAME button that did nothing. The v0.0.2 release build was driven through 11 deaths and 10 restarts in a minute with zero errors.

How these screenshots were made

Game screenshots are easy to fake, so here is exactly what was done.

Every image was captured from a real WebGL build running in a real browser (Vivaldi, which is Chromium-based, on a virtual display) at 1600×900, straight off the game’s canvas: no mockups, no paint-overs, no upscaling. A script supplies the keyboard and mouse input. Three kinds:

  • Unassisted, from the shipped build: the before/after pair (v0.0.1’s frame from the v0.0.1 build), the arena edge, and the death screen. Nothing but ordinary input; the v0.0.2 frames come from the exact files now at /play/.
  • Staged, from a developer build of the same commit: the hero shot, the field guide, the swarm and the fragments. A developer build adds a console that can spawn a chosen enemy, skip to a wave, kill everything on screen and pin intensity. It draws with the same code; it is just told what to put in the frame. Those captions say so, and say what intensity was pinned.
  • Beyond this version: the Metatron’s Cube image, pinned at an intensity v0.0.2 cannot reach. Labelled as such above.

The console is not in the game you play: the shipped files were scanned after decompression for its command names and contain none, while the developer build of the same commit contains all of them.

Changelog: v0.0.2

The full changelog lives in the game’s repository. Here it is in short.

Added

  • The void: a fractal background that breathes, drifts and zooms with intensity, coloured across Crystal-Void’s cyan-to-purple range.
  • Sacred geometry: the Flower of Life, always faintly present, with Metatron’s Cube wired to surface behind it at high intensity. Both keep a clear space around the player.
  • Neon halos on all five enemy types, one colour per type.
  • Particles: an ambient field around the player that thickens with intensity, plus death bursts.
  • Post-processing: bloom, chromatic-aberration pulse, hue drift, vignette.
  • Intensity: one value, built from your combo, that drives all of the above.

Changed

  • The visuals run at the Medium quality step instead of a forced Low, which had them at about 40% strength.
  • Chromatic aberration is capped at 0.20 (was 0.45), decided from side-by-side captures.

Known gaps

  • Nothing is saved. Lifetime score, runs and kills still reset when you reload.
  • The sacred geometry does not respond in play yet: a combo tops out near intensity 0.5, where the Flower is still at its floor and Metatron’s Cube reads zero.
  • The look is not final. Bloom, vignette, hue-drift speed and halo strength are working defaults nobody has signed off yet.
  • Death effects are small. The aurora ribbons, cog vortices and colour-matched death dissolves in the design are not built.
  • The Splitter does not split, the Ray Projector does not drop, and bosses, Void Entities and Cosmic Spirits exist only in data.
  • One universe. Only Crystal-Void is playable; the other four palettes have never been on screen.
  • Placeholder art remains for projectiles and the letterbox bars, and the HUD can overlap the player at some window shapes.
  • Performance has only been measured in software rendering. If the game stutters on your machine, we want to know.

Tell us what you see

This is the first version where the look is on the table, and the defaults are a builder’s guesses. The most useful feedback right now is about the picture: can you always find your ship? Does a busy screen ever hide an enemy that kills you? Is the void too loud, or not loud enough? Does it run smoothly on your hardware?

Play v0.0.2 at /play/ (desktop browser, keyboard and mouse), then tell us on the feedback page. There is no account and no captcha, and every message gets read.