This is the engineering side of WizusLabs, written in plain English. Where the guides explain our games to the people who play them, the blog explains how the studio works to the people who build things — long-form posts across five pillars: dimensionality, frameworks, data, craft, and the AI-native studio itself. Every empirical claim is sourced, every post stands on its own, and new pieces land here as we write them. You can also subscribe via RSS.
WizusLabs Engineering
WizusLabs Engineering Blog
Build-in-public writing about how an AI-native studio actually builds software — the engineering decisions, the trade-offs, and the honest detail behind the games we ship.
Subscribe via RSS — new posts land in any feed reader (Feedly, NetNewsWire, …).
Latest posts
No posts match your search.
Sixteen locales, one grammar: the localization assumptions English hides
A localization defect is rarely a translation error — it is usually a correct decision made in
the wrong grammar. Our games ship in sixteen languages; every string here was correct and every gate stayed
green. A screen reader spoke a plural noun over a count of one in French and Italian, and it fired on
every one-star clear. An ARB =1 selector chosen on purpose is rewritten by
gen-l10n into the CLDR one category, which in Hindi admits zero too — and
Hindi is no edge case but a four-member set including Vietnamese. Then the oracle: in English
incorrect contains correct, so an assertion meaning “this announced
correct” is satisfied by the announcement for incorrect in seven of sixteen locales. It fails
toward pass on the outcomes it exists to distinguish. Including why a prefix test and a suffix test partition
the corpus with neither sound everywhere.
Frameworks · 2026-08-03
Read back what you published: four ways the two store APIs disagree
Deciding to verify a store write is the small half. Comparing it on the right basis
is not, and the two stores need different equality tests on the same bytes: Play drops the POSIX trailing
newline on persist, Apple keeps that byte, so carry one store’s rule across to the other and you get 0 of 18
on a flawless upload — a fabricated catastrophe from a check that was working perfectly and asking the wrong
question. A first-hand account of one game’s copy and screenshots pushed to both stores in 18 locales, and
the four ways the two APIs disagree about what a write even means: what a body replaces, when a field is writable,
what order is kept, and what is atomic with what. Including the 409 that corrected a claim we had
already put in writing, and why 204 is the emptiest promise in HTTP.
Studio · 2026-07-31
Every zero has two explanations: the control your check is missing
A check has two ways to come back clean: the condition really is absent, or the instrument never ran. Both print the same zero. On one of our staging cuts the pre-promotion gate returned empty on a release binary that genuinely carried the marker — the output had been captured into a variable instead of piped. Four broken instruments, one indistinguishable result, and the control that separates them. Plus why one control is not enough.
Craft · 2026-07-30
Two homes for one rule: ‘extract’ is half a verb
A refactor is not finished when the new home works — it is finished when the old site carries no independent copy of the rule. Our own decision record moved an ad-eligibility predicate into one place so there would be a single home for it, named the original five times, called the duplicate removed, and filed the actual removal under follow-up, where it stayed. One page, two tenses. For the next three days both homes were live, and the next two fixes each went to the wrong address — both shipping with green tests, because a test that the new home behaves correctly passes just as happily with the duplicate still running. A person caught it, not a check. Why the guard has to be an oracle rather than a same-value check, how we bite-proved it on a private predicate no test could reach without mutating a line, and the third copy we found and deliberately left alone.
Craft · 2026-07-30
Pausing is the easy half
Gate the update loop and the game looks properly paused — which is exactly why the rest goes unnoticed. Everything that measures the game also has to agree it is stopped, and the pause has to know who is holding it. A first-hand account of rediscovering that shape in five of our own games in one week: a progression gate fed by a wall clock that kept climbing while the simulation sat correctly frozen; a win overlay that painted over a puzzle without absorbing taps, so two touches could restart a finished clock; an inner overlay closing and un-pausing the game underneath an outer one still on screen; and why we rejected the cheaper fix of subtracting the paused time.
Craft · 2026-07-29
A guard nobody has seen fail
One of our localization checks passed green for two days beside a file it could
not, in principle, detect — an app_ar.arb compiled into every binary that no player could
select — because the check enumerated the very list it was validating. A first-hand account of the two rules
that came out of it: derive the set you are checking from the authority (the disk, the call sites) and never from
the list under test, with every exemption declared data carrying a reason; and the one that matters more — a
guard nobody has seen fail is indistinguishable from a guard that cannot fail, so the deliverable of a new guard is
evidence it can go red, restored afterwards by hash rather than by eye.
Studio · 2026-07-28
Valid does not mean ready
Upload the first build to a brand-new App Store Connect record and it climbs to
processingState=VALID — the status is green, the spinner is gone, and there is still nothing you
can hand a tester. A first-hand account of the first-cut-only trap: why VALID is a fact about the binary, not about
whether a human can install it; why a fresh app stalls at MISSING_EXPORT_COMPLIANCE with no internal
beta group while its shipping siblings have one; the setup steps that make the first build testable; and why the
trap fires exactly once per app, which is precisely why it keeps catching people.
Studio · 2026-07-27
A debug switch that ships in the release build
We compile a debug switch straight into the release build we hand to testers —
an artifact identical in shape to the one that reaches the store — and the discipline around it is the only
reason that is safe. A first-hand devlog on the compile-time flag, the ahead-of-time quirk that deletes the very
token you would grep for (so DEV_UNLOCK is gone from both builds), and the
print()-side-effect marker that lets the binary prove which build it is: strings | grep
returns one in a QA build and zero in production.
Studio · 2026-07-27
The locale that breaks your layout
We laid our arcade game’s interface out in English, tested the overflow in
English, and everything fit — then localized it to five languages and watched a control quietly run off its
own edge in German. A first-hand devlog on the trap an English-only overflow test cannot catch: why a longer locale
(German compound words especially) overflows at the default 1.0× text scale before any accessibility scaling,
why a 2× English test is a different failure mode entirely, and the cheap parameterized guard — 42 cases
across the densest screens, in de, es, and pt — that made a longer
locale not “done” until its layout is verified.
Craft · 2026-07-27
Never name the other store
We ship the same game to two stores that each refuse to admit the other exists. A first-hand note on the cross-platform reference rule — why naming Android in an iOS listing gets it rejected under App Store Guideline 2.3.10, why Google enforces the mirror image, where the forbidden words hide (keywords, release notes, a Pixel in a screenshot), and the two-line grep audit we run before every upload so the same sentence that fails both reviews can never ship.
Studio · 2026-07-26
When two agents edit one file, the last write wins
Run two AI agents at once against the same file and you have not doubled your
output — you have started a silent argument about whose edit survives, with no merge conflict and no error
to warn you. A first-hand account of the shared working tree that makes hot files (game_bloc.dart,
the app root, pubspec.yaml) revert with no trace, and the three rules that fixed it: one owner per
file per batch, grep for a unique marker before you trust the change landed, and never “recover” with
git stash or reset --hard in a tree you share.
Studio · 2026-07-25
A version number is a promise; a build number is a receipt
Every app ships two numbers that look like one: the marketing version a human reads and the build number the store orders by. A first-hand account of why automation may bump the receipt but only a human moves the promise — semantic versioning as a claim about the size of a change, why “ship it” never authorises a version bump, and why the build number has to be the same integer on both stores or the crash reports stop making sense.
Studio · 2026-07-24
The permission you don’t use is a liability, not a convenience
We told Apple one of our games plays audio in the background; the game does the
opposite — it goes silent the instant you leave it. A first-hand postmortem on declaring an iOS capability
you don’t use (UIBackgroundModes: [audio]), the App Store Guideline 2.5.4 rejection it caused
in Sudoku on 2026-05-22, the identical rejection it caused in the tank game on 2026-07-09 because we fixed the
file and never wrote down the lesson, and the audit that finally made a habit into a rule.
Studio · 2026-07-23
One canonical home: the day we deleted 800 pages of our own site
We made our website easier to find by deleting most of it. A first-hand devlog on why two live origins — wizuslabs.com and the legacy github.io — were one site competing with itself for ranking signal, how we picked a single canonical home and stripped the other from 803 files to 7 (path-preserving redirects, removed pages 404→noindex), and the trade-off we took on purpose: no fallback host, a redirect shell to maintain, and a hard guard so we can never re-push the duplicates back.
Craft · 2026-07-22
Staging has to lie exactly like production
Why our test builds carry production third-party SDK keys, not sandbox — keeping the staging binary byte-identical to the one we ship, the “wrong API key” trap that blocks a tester before they play, and the single difference we always flag.
Craft · 2026-07-21
Why a well-behaved game goes silent the moment you leave it
A well-made game cuts its sound the instant you swipe away — and that silence is the most respectful thing it does. A first-hand devlog on pausing audio the moment focus is lost via Flutter’s app-lifecycle callback, why a game must never declare the iOS background-audio capability it does not use (both against the rules and contradicted by its own behaviour), and the recurring App Store rejection we caused twice before a local audit script finally made the fix stick.
Craft · 2026-07-20
Deterministic by design: seeding randomness so bugs reproduce
The most useful thing a game engine can give you is the ability to make a bug happen again, on command. A first-hand devlog on how our arcade engine gets there — one seeded generator owned by the engine, a canonicalized state hash that fingerprints a moment of play, and deterministic replay from a seed and a list of inputs — plus the sub-stream we keep deciding not to build, and the discipline tax the whole thing costs.
Frameworks · 2026-07-19
Consent without dark patterns: the first screen a free game is judged by
The consent screen is the first real thing a free game shows you, and it is exactly where free-to-play quietly turns adversarial. A first-hand account of how we wire GDPR/UMP consent and Apple’s App Tracking Transparency — no pre-ticked boxes, no guilt-trip asymmetry, the correct prompt order, and an honest ‘no’ that still works — including the trade-off we accept on purpose: a fair flow measurably lowers the opt-in rate.
Studio · 2026-07-18
The hidden cost of renaming a shipped app
We renamed a shipped game from Iron Blitz to Iron Swarm, and the new name was the easy part. A first-hand devlog on what a rename actually touches: canonical URLs baked into binaries already in the field, redirect stubs you must never delete, a version manifest that has to answer to both names, and the permanent identifiers — package, bundle, App Store number — a rename can never move.
Craft · 2026-07-17
Designing an app icon that survives the home screen
We had a Boardlore icon we liked at 1024 pixels — a gold knight on dark wood. Then we judged it the way players actually see one: masked to the iOS squircle and shrunk to a home-screen thumbnail. A devlog on why concept and legibility are two separate tests, why a pinwheel of pieces blurred to a blob while an open book survived, and why we redrew the winner coarse and bold to read at 120 pixels.
Craft · 2026-07-16
Write the canonical form: a lenient reader hid a mess
A strict validator refused an App Store link that every browser accepts — because the browser silently follows a 301 redirect the validator will not. That same forgiveness had been hiding an inconsistency across our own links: some canonical, some bare, none broken, none noticed. A devlog on lenient vs strict readers of the same data, and why you write the canonical form everywhere.
Craft · 2026-07-15
The browser tells the truth: a Back-button bug we almost fixed wrong
We had a confident, framework-deep theory for why the browser Back button did nothing on our Iron Swarm web build — and a fix designed and QA’d against it. Then we drove a real headless browser through the exact flow, and the actual cause was far simpler: a ‘Play on web’ link that opened the game in a new tab, where Back has no previous page. A devlog on reproducing before you theorise.
Craft · 2026-07-14
Designing games that survive interruption
Mobile games are played in stolen minutes, so the interruption — a call, a screen lock, an app switch, an OS kill — is the normal case, not the edge case. The craft of surviving it: treat the fragment as the unit of play, save on the way out and never on quit, pause the instant focus is lost, and resume without dropping the player into a live hazard.
Craft · 2026-07-13
Designing game audio that players don’t mute
Most phones live on silent, and the mute toggle is the most honest review a game’s audio will ever get. The craft of sound players keep on — design for silence first, never punish with audio, and spend loudness like a budget, with the fanfares saved for moments that deserve them.
Craft · 2026-07-12
One codebase, two stores: what actually differs shipping iOS and Android
A first-hand map of what really forks when a single Flutter codebase ships to both the App Store and Google Play — code signing, store review and tracks, metadata and screenshots, in-app purchases, build-number parity, and privacy declarations.
Frameworks · 2026-07-11
The first 60 seconds: designing a game’s first-run experience (FTUE)
A player decides whether your game is worth their evening in about a minute. Here is how a first-run experience earns that minute: orienting without a wall of text, the first meaningful action, the first win, and a reason to come back.
Craft · 2026-07-11
How we generate and grade Sudoku puzzles
A first-hand look at how our Sudoku app builds puzzles: fill a complete grid, dig holes while preserving a single solution, verify uniqueness with a solver, and grade difficulty by the techniques a puzzle demands — not by counting clues.
Frameworks · 2026-07-10
The small-game discovery problem: what Steam’s data says about getting seen
Discovery, not development, is the wall for small games. A sourced read of Steam’s 2025 data — ~19,000 releases, roughly half seen by almost no one, and the levers that measurably move visibility.
Data · 2026-07-10
An AI QA gate for games: what it catches, what it misses
A first-hand, honest account of the automated QA gate every change clears before we ship a game — what it reliably catches, and the things it structurally cannot see, which is why a human still signs off.
Studio · 2026-07-10
Designing games colorblind players can actually play
Around 1 in 12 men and 1 in 200 women have some colour vision deficiency, and color-only encoding quietly locks them out. Here is why it fails, and the practical design fixes — shape, label, contrast, safe palettes, colorblind modes, and simulator testing — that let everyone read the game.
Craft · 2026-07-10
Difficulty done right: curves, rubber-banding, and respecting the player
A good difficulty curve is a conversation, not a wall. Here is how the ramp, plateau, and recovery work, when dynamic difficulty and rubber-banding feel cheap, and the difference between a game that is hard and one that is simply unfair.
Craft · 2026-07-10
What’s charting and why — July 2026
Our first monthly read on what is actually charting and why: Cyberpunk 2077 back above 100k concurrent on Steam, the dated PC peak leaderboard (CS2, Dota 2, PUBG), and June 2026’s top mobile games by rank.
Data · 2026-07-09
What 2026’s top games have in common: a data teardown
A sourced teardown of the patterns that actually unite 2026’s top games — the $197B market and its platform split, free-to-play monetization, and why retention is a percentile band, not an average.
Data · 2026-07-09
Shipping web games in 2026: HTML5, WebGL, WebGPU, and Flutter-to-browser
The web-game rendering stack in 2026 — Canvas2D, WebGL, and a WebGPU that now ships in every major desktop browser — plus HTML5 distribution, the mobile support gap, and what it takes to ship a Flutter/Flame game to the browser.
Frameworks · 2026-07-09
How 2.5D games fake depth: parallax, billboards, and isometric tricks
2.5D is the art of faking depth without paying for full 3D — how parallax scrolling, billboarded sprites, isometric projection, pre-rendered backgrounds, and layered dioramas each conjure a third dimension, and when to fake it.
Dimensionality · 2026-07-09
Free-to-play, done honestly: ads, IAP, and not being evil
How a small studio funds a free game without dark patterns — honest ad-supported play, optional Pro to remove ads, and no pay-to-win, timers, or loot-box coercion.
Craft · 2026-07-09
Why we build in Flutter and Flame (and where it breaks down)
A first-hand account of why our studio builds its games on Flutter + Flame — one codebase for iOS, Android, and web, fast iteration — and an honest look at where it breaks down.
Frameworks · 2026-07-09
The anatomy of ‘juice’: why one mechanic feels alive and another dead
Two games can share the exact same mechanic and one feels alive while the other feels dead. The difference is juice — the stacked layers of feedback around an action, and when it tips into noise.
Craft · 2026-07-09
Unity vs the alternatives in 2026
Unity, Unreal, Godot, GameMaker, Bevy, Flame and the rest in 2026 — with the one caveat most engine comparisons skip: the ranking flips depending on whether you count Steam releases, revenue, or a developer survey.
Frameworks · 2026-07-09
Building games with an AI agent crew: our actual pipeline
A first-hand, honest account of how an AI-native studio actually ships games: an orchestrator plus specialist agent crews, review gates, and a human who stays accountable — including where the model works and where it breaks.
Studio · 2026-07-09
2D vs 2.5D vs 3D vs “4D”: what game dimensions actually mean
What 2D, 2.5D, and 3D really describe under the hood — and the honest truth about the “4D” label that gets sold hardest and understood least.
Dimensionality · 2026-07-09