WARBIRDS.IO Dev Log

← All posts

The flying billboards

The Brick Sea got its first civilians who fly: Barnstormer Bess, a cream-and-gold biplane dragging the sponsor’s cloth, and Marquee Mo, an ad zeppelin with the message painted on both flanks. They tour the landmarks, sell things nobody in a warzone needs, and if you shoot one down — and you can — the sponsors just send another. The copy itself lives in a database table, so changing what the sky is selling is an UPDATE, not a deploy.

A cream-and-gold biplane towing a banner reading EAT AT BRICK'S over the sea, an ad zeppelin with BUY WAR BONDS on its flank beyond

In the war but not of it

The whole trick is that these two are ordinary planes to every shared pipeline — the same flight model, the same snapshot wire, the same damage and wreck physics — and invisible to every system that decides things. They fly for a third, teamless team, so anyone’s rounds connect; and every AI targeting loop skips them: bot pilots won’t waste a pursuit, flak batteries let them drone past, and the searchlights — which would otherwise park every beam on a zeppelin loitering over the ring all night — hunt combatants only. They don’t hold the Sky Ring, don’t count toward the lobby’s bot population, don’t appear on the scoreboard, and read as a small gold ring on the minimap instead of a bandit blip.

Popping one pays a token five points to your side and a marquee line in the feed — never a scoreboard kill, a career stat, or a nemesis grudge. The wreck tumbles like any airframe (a falling zeppelin is worth the ammunition on its own), and thirty seconds later the replacement drones in from over the horizon flying the same campaign, because a shootdown shouldn’t censor the sponsor mid-round. At the bell, every craft cycles to the next message in the rotation.

An airship the flight model already knew how to fly

There is no buoyancy term in our six-degree flight model, and adding one would have meant touching the physics that must stay bit-identical between Go and the browser. The Marquee instead is configured, not coded: 320 m² of “wing”, nine tonnes, huge inertias, tiny control authority, and a thrust curve that tops out at 32 m/s. It stalls at 20, cruises at the firewall, and turns like the building it is — which is exactly the look. The biplane is the same recipe pointed the other way: light, draggy, all lift, happy at 50 m/s and unstallable in normal service. Zero new physics; two new rows in aircraftTypes.

The probe that tuned them steps the real world for five simulated minutes with a full 36-bot lobby and logs what the circus actually did. The biplane held 376–399 m across the run (it likes a cushion over its 350 m target — fine), worked five of its eight tour stops in those five minutes, and took zero stray damage through a live furball. The zeppelin held 618–705 m around its 680 target and lapped all six of its Sky Ring stations. Nothing shot at either of them. The war, correctly, does not care.

The copy is a table

Banner text comes from a new ad_banners table: one row per message, an enabled kill switch, a sort column for the rotation. The server seeds it from the config’s fallback list on first boot, re-reads it every minute, and never lets a flaky database blank the sky — the last good rotation keeps flying. Open it in DBeaver, insert a row, and within a minute a biplane is towing your words across the Brick Sea. No database configured? The fallback list in shared/config.json flies forever.

Confession

Two things the process caught. First: the screenshot rig that shoots the about-page gallery had been quietly broken since the Vite build landed — the injected director still imports bare three, and the import map that resolved it left with the old HTML. Chromium accepts import maps added after load now, so the rig hands the director its own; the gallery pipeline works again, which is how this post has a picture. Second: the first hero shot of the banner came out mirrored — the camera was parked on the cloth’s back, which is exactly what a real towed banner looks like from the wrong side and exactly what a marketing shot shouldn’t do. The circus, at least, would have approved of the honesty.