Each leaderboard is now a single screen, and you switch the scope inside it. Open Highest Level, Weekly Missions or Total Stars and move between Global, Country and State without closing anything. It used to be the other way around: the country and state rankings were separate screens, so comparing your level worldwide against your level in your state meant leaving one and opening the other.
The weekly leaderboard now shows how much time is left. A counter in the header — "2d 14h" while there is room to spare, "18m 42s" when it is time to hurry. There was no way to know when the week rolled over before.
The weekly leaderboard now counts people who play
Completing different missions during the week is enough to place you. You previously had to beat a star record to show up — so someone who finished eight new missions in a week without improving a single star stayed out of a ranking ordered by exactly those eight missions. Nobody who appeared before drops off.
Replaying the same mission no longer counts twice. The weekly mission count now counts distinct missions, so the ranking measures how far through the game you got, not how many times you repeated the fastest level.
Coins now reward progress
You earn coins when you beat your own best — more stars or a better time on that mission. Finishing again without improving pays nothing. Every completion used to pay, so replaying the easiest mission was unlimited income, which made every price in the shop meaningless.
Two different achievements on the victory screen, where there used to be one. "New Personal Record!" when you beat yourself, and "New Record!" when you take 1st place on that mission across all players. Only the second badge existed before, and it showed on every replay for whoever already held the top spot — including on a run that was worse than their previous one.
Fixes
The global leaderboard showed Portuguese to players on other languages. "Nível 42", "8 missões" and the empty-ranking notice were hard-coded in Portuguese on the global leaderboard screen, in English, Spanish and French. All three now follow the chosen language.
The coins shown on the landscape victory screen were computed separately from the amount actually credited. They are now the same number.
The screen jumped when switching from World to Country or State. The region name only appeared under the title on the regional scopes, which pushed the countdown, the tabs and the list down by 12 pixels — with the list losing that same height, since the card has a fixed height. All three scopes now show the legend, and nothing moves when you switch tabs.
Internal Changes
One leaderboard modal instead of two. 833 lines became 436. The two old ones were near-copies with duplicated loading state, podium row and empty list, and that duplication is what let the language fix land in one and not the other.
The countdown mirrors the clock that defines the week, including the mix of local time and UTC it carries — fixing it would move every player's progress to a different week. A test enforces that the two agree at the rollover.
The weekly counter gained a list of missions already counted, and it stays incremental rather than deriving from that list: deriving would drop whoever updated mid-week from 40 to 1. It coexists with the previously installed version without either side overwriting the other.
The offline sync queue was sending fields one by one when restoring stats, which made a new field vanish from the cloud with no error at all. It now sends the whole set.
personal_record was added to the level-complete event, which makes the drop in coin-event volume readable — without that dimension it would be indistinguishable from a defect.
Every ship now has light and shadow. A bright edge on the side the light comes from and a dark one opposite it — the same light that lifts the planets, and it stays put in space while the ship spins in orbit. The ship stopped being a flat silhouette and started reading as an object.
All 13 ships are now the same size on screen. They were drawn at noticeably different sizes, and the starting ship was the smallest of them all — switching ships changed how big your ship was, with no warning. They now take up the same space, including in the shop, which shows the real proportion.
None of this changes difficulty. The collision area is identical across all 13 ships, as it always was.
The sound themes have new names
Prism, Bell, Bronze and Triumph are now Aurora, Crystal, Forge and Apogee. Only the name changed — each theme sounds exactly the same, and the theme you picked is still picked.
The selector now marks which one is the default.
Fixes
Ship launch in the Crystal theme sounded like a slide whistle. It was the only one of the four behaving that way; it now belongs to the same family as the other three, without losing the shimmer that sets the theme apart.
Internal Changes
Ship volume is baked once per level, not drawn every frame. The ship became a cached sprite, so the effect costs less per frame than the previous drawing did, while being richer. Measured on device: zero dropped frames.
render_ship_quality in Remote Config turns the effect off without a new release — 0 returns to the previous drawing, 1 uses a single sprite with the light fixed to the hull.
Ship brightness is now measured, not estimated. Sampling luminance from the device screen showed the edge at 224 against 143 for the brightest planet; it now sits at 212, with 94% less bright area.
New development bench (yarn ship:lab) with all 14 shapes side by side, the real proportion of the planets around them and luminance readouts — it does not ship in the published app.
A test now measures each ship's silhouette through the real renderer, which keeps a new skin from landing at the wrong size.
New item in Settings: Sound Theme. Four sound sets to choose from, and the choice applies to the whole game — launch, crash, reaching the planet, stars, lock and the unlocked level. Nine sounds per theme.
Prism (default) — warm body with a discreet shimmer on top. It is the sound the game already had in play, now with the celebration matching it.
Bell — crystalline, with a long tail.
Bronze — the same shape as Bell in a darker timbre, with no treble.
Triumph — closest to the sound of reaching the planet, which is the moment the game always got right.
The choice follows your account, so switching devices does not reset it.
Fixes
The celebration is no longer louder than the rest of the game. It was coming out well above the ship launch — more than triple at the moment the next level unlocks. All sounds now sit at the same level, measured.
The celebration no longer sounds out of tune. The chords for unlocking a level and clearing a sector used a progression that never resolved, and it clashed with the background music. They were rewritten to fit the soundtrack at any point in it.
The lock burst sounded like a dry kick drum and came in too loud. It now settles instead of punching, and has a tail instead of ending at the impact.
"Sound Effects" in Spanish was written in Portuguese.
Internal Changes
Volume is now measured, not estimated. A test reconstructs each sound's envelope and integrates it — that is how the gap above was found. Adding up the gains was useless: the celebration stacks up to twelve oscillators while a round uses one or two.
Declared chords are verified. A test rejects any chord with two notes too close together, which was exactly the defect shipped in 1.4.1.
Sound bench (yarn sound:lab) for comparing the sets against the animation with the music playing underneath. It stays out of what is published, by construction.
The end-of-level celebration got its sound. The animation shipped in 1.2.1 and was silent: stars lit up, the padlock broke and the new sector opened with no audio at all. Each of those moments now has its own sound, fired at the exact instant of the matching animation.
The three stars rise in pitch. They play a rising phrase rather than three identical chimes — the third star is the one you came for, and now it sounds like the hardest of the three.
The music makes room and comes back on its own. The background track dips during the celebration and returns to normal at the end. Without that, the celebration shared the same range as the synth and came out as a denser mix instead of a moment.
None of it sounds off-key. The sounds were written in the same scale as the music playing underneath, which is the only condition where tuning problems show up.
The Sound Effects switch applies here too. With it off, the whole celebration runs in silence.
Players who prefer less motion hear a single sound. With "reduce motion" enabled the entire sequence passes in a blink — so instead of seven sounds at once, only the one that sums up the achievement plays.
Internal changes
The site got a changelog page, in Portuguese and English, generated from these files and linked from the footer and the news block. It used to be maintained by hand and drifted because of it: the landing pages spent two releases announcing "version 1.3" while the game was already at 1.4.0. A gate guard now blocks the release until the page matches the text here.
The landing pages stopped hardcoding the version and now describe the current state of the game. The App Store and TestFlight badges were aligned with the rest, and the changelog page's emoji became icons.
Only the chosen sound set ships. The five alternatives that were tested and the bench that compared them stay out of the published build — 1,327 bytes the player does not download.
One switch instead of two. "Show name" and "Show photo" became "Join rankings anonymously". Half anonymity protected nobody — a Google profile photo identifies you just as well as your name does. Anyone who had already unchecked either one joins anonymized, with nothing to reconfigure.
The preference is now actually honored. It existed and was ignored in three independent paths: the global ranking was rewritten with the real name at the end of every level, the mission ranking never consulted the preference at all (and with no data fell back to the Google account name), and signing in with Google copied the real name into every one of the player's entries. A fourth path leaked through the offline queue: playing without a connection and reconnecting published name and photo.
Changing the preference now reaches what is already published. Each ranking document keeps its own copy of the name, so changing the setting was not enough. The global board, the weekly boards for every week, and each mission board are all rewritten now — and the rewrite retries until it succeeds.
The Profile screen shows what goes public. A new block displays exactly the name and photo other players see, with the anonymous name generator next to it when that is what applies.
Improvements
The Profile screen fits without scrolling. On a Redmi Note 10 (393×873) the content ran 105px past the visible area, leaving the privacy controls below the fold while the Save button stayed in view — the screen looked complete and wasn't. It now has room to spare even on a 360×640.
Your own row stands out in the rankings. On the global and regional boards your entry shows in blue, as it already did at the end of a mission. The "your position" block at the bottom now appears only when you are not in the list.
Internal changes
Public rankings stopped carrying data nobody read. City, time zone and country name are gone (the country code stays, for the flag, and so does the state, which feeds the regional ranking), along with perfectMissions and totalPlayTime — both fed only into the score calculation and no screen ever used them.
check:ranking-privacy joined the gate. It rejects any write to a public collection that assembles a name, photo or location outside the single point of decision. It found a leak the manual review had missed.
yarn firestore:backfill-privacy rewrites documents already stored, to reach players who never come back. Dry-run by default.
From 30 to 95 fps on a Motorola Edge 60 Neo. The engine was painting the equivalent of ~2.45 full screens per frame, and 85% of that was two full-screen passes with a gradient shader. 32 ms per frame before, 11 ms after, with the GPU at a 4 ms median. Measured with dumpsys gfxinfo on a release build, not on an emulator.
The background became a cached bitmap. The full-screen radial gradient was rebuilt every frame; it is now pre-rendered per (theme, backbuffer size) and blitted 1:1. The cost was recent and went unnoticed because the branch was dead: only 8 of the 18 themes declared a gradient, and the first reachable one belonged to level 1001 — until then the real path was a solid fill.
The vignette left the canvas and became CSS. It cost a full screen of alpha-blended gradient per frame to deliver a peak alpha of 0.053, and only in the four corners. Parity checked against the browser render: 0 of 367,928 pixels with a perceptible difference.
One dropped frame per second that nobody could see. The render loop is recreated every second by the mission timer, and the effect body rewrote canvas.width — which clears the surface even when the value is identical. It showed up as a stutter in the ship's movement, not in the average FPS.
Refresh rate
The game unlocks 120 Hz on panels that support it. It was stuck at 61 fps, and it was not a render limit: the window never asked Android for a rate, and with no request the system assumes 60 Hz. Nothing changes on a single-rate panel.
Screen shake and trail density no longer depend on frame rate. The shake lasted twice as long on a device running at 30 fps, and the trail came out thinner. Both are measured in time now. The physics simulation already was, so trajectory, orbit and time records are unchanged.
Internal changes
Agent guide reconciled with the repo (i18n, test, script and line counts), plus two documented traps: the landing pages are mirrored siblings living outside format:check, and the @DEV_ONLY marker is inert — the block it wraps is public.
Landing pages describe the current state of the game.
Section written on 2026-08-07 from the commits: the release was cut with no CHANGELOG entry.
Store
The store had 30 cards and zero purchasable items for a new account, and the navigation axis was item type — a new player landed straight on "Equip". Reorganized, with a "New" badge so the sector reward becomes findable, and a path to the coin store, which simply did not exist.
Tapping a card now shows the item on the player's own ship, instead of on a detached preview.
Two quick taps bought two ships for the price of one.
Preview fixes: the ship was a 48 px bitmap upscaled 2×, the 750-coin trail rendered as flat orange, the thumbnails diverged from the in-game ship, and "New Sentinel" and "Emerald Green" were the same green rocket.
Economy and rewards
Completing sector 1 now grants a ship, instead of only unlocking level 26.
The player is told about the reward, and one notice stopped erasing the other.
Price multiplier via Remote Config, to answer "did I overprice this?" without republishing to the store.
Visual
Planets stopped being flat circles: atmosphere, day/night terminator and rim light, with the light fixed in world space.
The background gradient stopped at level 400, and the rings rendered flat.
The free cosmetic pack stopped looking unfinished.
Particle splash on the trail's stars, with a better-chained burst.
Fixes
The mechanic tutorial reopened on every new session — the guard lived in memory, and with a 25-level window the tutorial came back for as long as the player stayed in the sector.
White bands on the system bars on an outdated WebView.
Internal changes
Eleven analytics events so the store stops being tuned in the dark, the native Firebase Analytics plugin in both projects, and events now go to the Android stream with the push payload behind an allowlist.
Color manipulation centralized in colorUtils, with tests.
Victory now happens on the trail. "Continue" returns to the map instead of going straight to the next level: the stars you earned light up one by one, the next level's lock breaks, and the player decides whether to play. Skippable with any tap, and it respects prefers-reduced-motion.
This is a flow change, and it is deliberate: it costs one extra tap between levels, in exchange for 20 passes through the trail in a 20-level session — which is where the store, energy, rankings and inbox live, and none of them had any presence in the flow before. Worth measuring with the end-of-level event that already exists.
The locked-level node became visible again. Its fill was exactly the background color (1.00:1 contrast, against WCAG's 3:1 minimum) — breaking an invisible lock over an invisible node did not read as anything.
Star field with real depth
The sky was flat from level 101 onward. Per-distance brightness was assembled by string substitution and only worked with rgb() colors; 17 of the 18 background themes declare hex, so the opacity was never applied — with no error and no warning. Since the background changes every 4 sectors, only levels 1–100 had depth.
Coherent depth: size and speed now derive from a single depth. They used to be independent draws, so a star could be large and slow — the eye reads size as "near" and slowness as "far", and the contradiction flattened the scene.
Twinkle with its own phase and rhythm per star, and 4-pointed stars in the nearest 7%. All of it off under performanceMode.
It ended up cheaper than before: the loop is now per opacity band — 6 fillStyle and 6 fill() per frame, against up to 300 strings and 300 fill().
Internal changes
utils/levelCelebration.ts decides the celebration script as a pure function, with 10 test cases — including replaying without improving, and crossing between sectors.
The Android release stopped depending on the iOS toolchain (android:sync:*).
A large release: 43 commits since 1.1.7. It satisfies two Play Store policies with a deadline, fixes player-data loss and leakage, and brings the game up to the behavior Android 16 requires.
Play Store compliance
Target API 36 (Android 16) via Capacitor 8, with the whole AGP/Gradle matrix following along. Without it, no update could be published after 2026-11-01 — which would also block the pending security fixes.
Google Play Billing Library 8, via RevenueCat 13.x. Same deadline, independent policy.
16 KB page alignment verified on the artifact, not merely declared in the manifest. The app has no misaligned native library today, and the check runs on every release build so the first plugin to bring one in cannot slip through.
Fixes that affect player data
Progress is no longer overwritten by reset progress. Three defects in the profile write path, all found by invariant tests over sequences: the offline queue lost operations to a race, replayed an old snapshot over newer data, and a default profile wiped the coin balance. Progress is now reconciled by join — records by maximum, best times by minimum — and not by the clock.
The offline queue no longer crosses between accounts. A queue created in an anonymous session drained inside whichever account signed in afterwards, carrying the displayName along with it.
The ranking cache leaked another account's rank and name, on anonymous → Google sign-in and when opening the rankings before auth resolved.
Reads on /users restricted to the owner in the Security Rules.
permission-denied stopped disguising itself as a network error — a rules failure was indistinguishable from an empty list, and that is how one regression got through.
Gameplay and economy
The mechanic tutorial shows up at all six milestones again. The previous version required the mechanic to be in the sector's first level; since generation is deterministic, moons, portals and orbiting systems landed on levels without the mechanic and the tutorial was discarded for every player.
Star difficulty became tunable via Remote Config — damage margins, difficulty boundaries and time thresholds moved out of literals in the code.
The daily reward now requires two consecutive days.
Values published in Remote Config reach the game again, with the guard now requiring both an override and a fetch. And the ad cooldown default respects its own floor.
Visual
The HUD respects insets in edge-to-edge, mandatory from target 36 on.
Build security
Production build hardened: no WebView debugging, no cleartext traffic, no automatic backup.
Signing secrets out of the bundle and with no default value in Gradle — a release task without them fails immediately, telling you what to set.
Internal changes
firestoreService split into 13 modules by domain; source tree moved into src/; level geometry, mission economy and the modal tree extracted from App.tsx into their own modules with tests.
A 9-step quality gate, including five in-house validators (i18n, remote config, version, shared constants and the script catalog).
firestore.rules behavior frozen by a characterization test against the emulator.
package.json scripts reorganized by destination, with a catalog in yarn scripts.
Reward cooldown via Remote Config: the rewarded ad cooldown became remotely configurable (rewarded_ad_cooldown_seconds), with a 180s safety floor applied in applyConfigOverrides
Note: version 1.1.4 was skipped in the numbering — there was no release.