What it is
TBA (TaskBarApocalypse) is a wasteland-themed idle RPG that sits in your taskbar. A three-person squad marches, meets enemies, fights and picks up gear inside a transparent 760×200 strip at the bottom of the screen. You work; they level. The design has three loops: real-time automatic combat for the main party, a hideout you build up, and an expedition system unlocked mid-game (send party members away for 8 to 12 hours, get back a batch of high-tier gear of a specific type).
The brief we set ourselves: small and polished, word-of-mouth retention first, local first, no central server, no real-money market. The inspiration is the existing genre of taskbar idle games, and the order of magnitude of the numbers references their public data, but every absolute value gets re-anchored to our own pacing.

Rules first, code second
Before writing anything we wrote a technical design and hand-off document that fixed the architecture and a few iron rules. Every development task since has referenced it.
Four layers, one-way dependencies: presentation → simulation ↔ data / persistence. The presentation layer only reads the simulation; the simulation knows nothing about any visual node.
Four iron rules:
- The simulation runs on a fixed timestep accumulator (one tick = 0.1 s), decoupled from render framerate. Shrink the window to nothing or hide it and the logic keeps going; offline catch-up is just "fast-forward N ticks".
- The simulation is deterministic: same seed plus same inputs gives identical results. Randomness uses a serialisable seed that goes into the save together with its state.
- The save file is the single source of truth. With no server reconciliation there is no local-versus-server mismatch to worry about.
- No anti-cheat. Save validation is light and exists only to stop a corrupted file from crashing the game. Changing your clock only hurts you.
Development was then cut into vertical slices that each run end to end, headless simulation with placeholder art first, presentation layered on afterwards:
| Slice | Content | Status |
|---|---|---|
| 0 Skeleton | Godot project, directories, fixed tick loop, debug panel | Done |
| 1 Headless combat | Three-tier stats, damage formula, mitigation pipeline, drops into inventory | Done |
| 2 Economy loop | Supplies / gear dual engine, hideout and workbench, region depth brake | Done |
| 3 Character compositor | 5-layer rig + 5 states + per-frame pivots | Proven, then shelved |
| Presentation | Pixel-art battle stage wired to real numbers and the economy loop | Playable |
| 4 Expeditions | Dispatch, deployment lock, real timestamps, tiered settlement | Not started |
| 5 Persistence | Local save, offline catch-up, taskbar window mode | Not started |
Slices 0 + 1: combat with no picture
The first version of combat was pure text. Three party members against five grunts, attack timers advancing every tick, the console printing one line per hit: "Melee hits D1grunt3 for 18 (HP 72/90)". The goal was singular: prove the math is right and the loop is fun before touching art.
Stats use a Diablo 2 style three-tier stack:
final = (base + Σflat) × (1 + Σadditive) × Π(1 + each multiplicative layer)
The multiplicative layers are "several independent layers multiplied one by one", not summed and applied once. This is deliberate: the flywheel, and the feeling of "a full set of rolled affixes makes power jump", both come from here. The project runs a self-test on startup: base 10, flat 5, additive 50%, two multiplicative layers of 20% and 10%, expected 29.7, and it prints FAIL if it doesn't match.
The mitigation pipeline is strictly seven steps in order: dodge → block (flat reduction) → elemental resistance → diminishing physical armour → general damage reduction → absorb shield → floor (every hit does at least 1). The armour step is coupled to region depth:
reduction = armour / (armour + threshold), threshold = 14 × depth + 12
Armour equal to the threshold halves damage exactly. This formula is the mathematical hook that turns "depth" into a power gate: the same chest piece gets less effective the deeper you go, which naturally pushes you to re-gear.
Randomness is wrapped in a DetRNG whose seed and internal state can both be read and written, leaving the interface ready for the offline catch-up in slice 5.
Slice 2: making the economy turn on its own
The economy has two engines:
- Scale engine: clear a region for supplies → upgrade the hideout → percentage bonuses for the whole party, plus one more expedition slot every three levels.
- Gear engine: clear a region for materials → upgrade the workbench → craft → equip → power.
The two are coupled through expedition slots, and the brake chain is "region depth → required power → capped by crafting depth / workbench level". Speed is not limited by counting items.
Idle-time automatic spending is a deterministic greedy policy in fixed order: upgrade the hideout if you can, upgrade the workbench if you can, then try to craft one weapon. Crafting has one key rule: a crafted item is only paid for and kept if it is definitely better than what's equipped; otherwise it is discarded and no materials are spent. So when crafting hits a ceiling, materials naturally pile up and go into the workbench, the upgraded workbench then produces stronger items, and the two engines leapfrog each other without any extra scheduling code.
Verification is a headless run of 12,000 ticks (20 minutes of game time) ending in a report: wins and losses, deepest region reached, resource balances, hideout and workbench levels, inventory count. Two runs with the same seed produce byte-identical output; determinism passes.
Slice 3: a home-grown layered character, proven, then set down
The character scheme in the design document was elaborate. Five sprite layers (right arm / body / right weapon / left arm / left-hand item) stacked in z-order, no bone interpolation (a 40-pixel character jitters the moment you interpolate), and instead frame-driven animation with per-frame pivots: every frame reads a set of right shoulder, right hand, left shoulder and left hand coordinates, and the weapon and item snap to their pivots. The right-hand weapon class is the sole driver and picks the animation set for both arms; the left-hand item passively follows the left hand; the body is shared, swapping to a leaning body only for two-handed melee attacks.
We ran the whole thing with coloured rectangles as placeholders: five states, switching between four weapon classes, mirroring, pivot visualisation, all correct.

Then we made a practical call: shelve it. Getting the logic running is easy; drawing five layers times five states of pixel frames for every weapon class is a different order of work, and this is not the stage to spend time there. The blueprint stays, the code stays, and the player characters switch to "track B": whole-body frame sequences from an off-the-shelf asset pack. Both tracks are driven by the same AnimState enum, so switching back later won't touch the combat code.
A detour through art
At first we wanted AI to draw the pixel characters. Output was fast, but what arrived was fake pixel art: it looks like a grid, but the actual cell size is uneven, edges are anti-aliased, and one "pixel" contains several colours. Scale it back to native resolution and it turns to mush.
![]()
We wrote a small local tool for this: FFT the image's gradient profile to find the dominant spatial period, which gives the true grid scale and phase; take the dominant colour per cell to downsample to native resolution; optionally median-cut the palette and binarise the transparent background. Self-tests on synthetic fake-pixel images reconstructed correctly.
The tool works, but in the end we didn't go that way: the time spent fixing images isn't worth more than just buying. Current art comes from two asset packs: a post-apocalyptic themed pack (a modular hero made of a handless body plus weapon layers, three zombies, a large set of scenery objects) and a dark-series monster pack (twenty-odd monsters as 90×90 single-row strips, four sets of six-layer parallax backgrounds). Both are paid commercial licences that forbid redistribution, so the whole art/ directory stays out of git.
The packs have inconsistent filename casing and encode frame counts in filenames, so we wrote an ArtDB that indexes everything lowercased and solves this once. It currently indexes 1,338 clips.
The presentation layer: what it looks like running today
The main scene is a transparent 760×200 strip with Godot's per-pixel transparency enabled. Inside is a march → engage → fight → victory / defeat loop:
- March: six parallax layers scroll by factor, 0.02 at the far end and 1.0 up close; the sky layer can be toggled.
- Engage: enemies walk in from the left, the melee fighter charges, the pistol and rifle users stop at their respective ranges.
- Fight: every hit calls the simulation's
Damage.resolve_hit, the same math as the headless run, only the choreography is now framerate-driven. Bullets actually fly, casings actually eject and land, hits flash white with a 1-pixel knockback, and there are damage numbers, block marks and MISS text. - Camera: centres on the party when there are no enemies, on the midpoint between party and enemy group when there are, with a dead zone and a speed cap so a single enemy dying doesn't jolt the view.
- Coordinate rebase: after each wave the whole world is shifted back to standard slots and the camera shifted the same distance the other way. The picture doesn't move a pixel, but party members never have to "walk backwards" to reset.
The economy loop is wired in: wave drops go into the inventory, supplies and materials are credited, auto-upgrade and crafting events pop up as toasts. Meeting the power threshold advances to the next region and swaps the background; losing retreats one region to farm. There are 8 regions of 10 waves each, and wave 10 is a boss at 1.5× scale. A downed party member lies where they fell and revives after a 12-second charge, with a small crying portrait in the top right; out of combat they regenerate 5% per second, in combat none.

Key decisions
| Decision | Choice | Why |
|---|---|---|
| Rendering | Two tracks: CharacterRig (shelved) / ActorSprite (in use), same AnimState | Keep the blueprint, ship visuals with existing assets first |
| Party | Bat / pistol / rifle, one body distinguished by tint | The asset pack has one hero body |
| Death | Downed awaiting rescue, revives in place after a charge | Doesn't interrupt the idle rhythm |
| Numbers | Presentation and headless share Combatant / Damage | Two sets of math will drift apart sooner or later |
| Art | Paid asset packs, kept out of git | Licence forbids redistribution |
Toolchain
Engine Godot 4.6, language GDScript, code written by Claude Code from the command line. An MCP plugin in the editor lets the agent drive the editor directly to run scenes and take screenshots, so nobody has to alt-tab back and forth. Headless verification runs with godot --headless, prints its report and exits on its own.
Next
- Tuning the pacing: monster growth, drop rates and region thresholds are all still placeholder magnitudes.
- A UI shell: side panel and base entry so the player can see the party and the bag.
- Slice 4 expeditions; slice 5 saves + offline catch-up + real taskbar behaviour (borderless, always on top, docked to the bottom edge).
This article was compiled by Claude Code from the project source and design document. Screenshots are from the current build.