Cross-Platform App
Extrema Arcade
A calculus game from my tutoring site's demo page, rebuilt in Flutter for Android, Windows and web, and checked against the browser original by running the original's own source code, rather than by playing both and deciding they look the same.

Employer signal
What This Project Shows
A port is a claim, that the new program computes what the old one computed, and the usual way to check it is to run both and look. Here the claim is tested: the original's own source text is the oracle, and the Dart engine has to reproduce it to 1e-9, curve included, before any of the game around it counts as working.
Problem
What Needed To Be Solved
The game teaches a distinction students get wrong: whether a peak is the global maximum is judged over the window on screen, not over the whole curve. Its levels are generated rather than authored: bumps summed from a seeded PRNG, turning points found from sign changes of a numerical f', and a set of fairness rules that throw a whole level away if two points sit closer than 1.4 domain units or if any grade is a close call. That tuning is the game. A port that transcribes the algorithm slightly wrong does not crash and does not look broken; it quietly generates different levels, with the difficulty curve and the fairness rejections gone. The failure has no symptom, which is exactly what makes hand-porting it risky.
Approach
How I Built The Solution
So the test suite never compares the port against a second transcription of the algorithm. A Node script slices the pure level-generation section out of the shipped demo.js between two literal markers, evaluates that exact text, and dumps levels 1 to 8 as JSON: the tool reimplements nothing. The Dart tests then assert that the independently written engine reproduces that file to 1e-9: 25 samples of f(x) per level as well as every point's position, height, grade and decoy flag, so a port that arrived at the right answer key from a subtly different curve still fails. On top of that diff the suite re-derives the answer key from scratch at a finer scan than the generator used, which checks the invariant the original was built on instead of merely its output.
Outcome
What It Demonstrates
All three targets were built from the one codebase on 2026-08-04 (an Android debug APK and a Windows binary, both still on disk) and the project carries android/, windows/ and web/ directories with no ios/, macos/ or linux/, so three is the claim rather than a framework promise. The Windows binary and the web build were both run that day; the web one is how the layout bug surfaced. The APK has only ever been compiled, never installed on a device, so nothing here says it runs on Android. There are no package dependencies beyond the two the Flutter scaffold generates, and 36 tests: 24 on the engine, and 12 driving the real widget tree, six of which are rendered screenshots, two of the states captured in both themes. It was my first Flutter app, built in a single working session, AI-assisted throughout, which is the argument for pinning a port to the original's own source text rather than to anyone's reading of it, mine or a model's. What it is not: it is not published to any store, scores do not persist across launches, the other demo on the tutoring site is not ported yet, and there is no CI, so the suite is only as current as the last time I ran it. Levels past 8 are covered by invariants rather than by the golden file, and the extractor has to re-declare one constant by hand because that constant sits above the slice it takes: a seam I would close before trusting this pattern on anything larger.
Evidence From Source
The oracle is the original's source text
The generator string-slices demo.js between two literal markers and evaluates it; nothing in the tool re-states the algorithm, so the comparison is against the shipped program rather than against my reading of it, and that program is still live and playable at danlitvak.github.io/tutoring/demos/extrema-arcade, so the thing the port is measured against is public. The Dart side asserts 25 samples of f(x) per level alongside every point's x, y, grade and decoy flag at 1e-9: the test carries the comment "Same curve, not merely the same answer key", because matching answers on a subtly different curve is the failure that would otherwise pass.
A precision bug that raises nothing
mulberry32 is defined by Math.imul and >>>0, and Dart has neither natively; on the web target its int is a JS double, so a 32x32 multiply written the obvious way would silently lose precision and drift level generation away from the web version without ever throwing. The port keeps every intermediate under 2^53 instead. The expected values in the test are the JS sequence itself (seed 12345 gives 0.9797282677609473, 0.3067522644996643, and so on) captured from the generator running in Node rather than read back out of the Dart code under test, so anyone with a JS console can re-run them. The honest limit: flutter test runs on the Dart VM with native 64-bit ints and the project has no browser test configuration, so bit-identity on the web target is argued from the code rather than demonstrated by a run on that platform.
The suite re-derives what it is checking
Beyond the golden diff, the invariant tests re-scan each point's visible window at 2000 samples, finer than the 320 the generator used, and settle global versus local from the curve itself, check that f' changes sign across every turning point, and check that every decoy is steep enough for "neither" to be fair. A golden file only proves the port matches; this proves the property the game is graded on still holds.
A layout bug that reading the code did not find
The top bar, HUD, result line and three button rows were all fixed-height, so a 310-pixel-tall window overflowed the column by 190 pixels and pushed the answer pad out of reach entirely. It surfaced from running the web build. Control sizes now derive from the height actually available, but the first fix rested on a wrong assumption: the start overlay was taller than a short viewport in its own right, and then the top bar's title wrapped at narrow widths, so the chrome the column budgeted for as fixed-height was not fixed at all. The title and every HUD label are now single-line with ellipsis, the chip group scales inside a FittedBox, and the overlay scrolls under a min-height constraint so it still centres when it fits. Two regression tests hold it: one pumps nine viewports from 144x380 to 1280x800 and requires both that nothing threw and that "Start" is still findable; the other checks that each of the five answer labels lands inside a 375x430 window rather than below its bottom edge, because a button that exists offscreen cannot be tapped.
A widget test that agreed with itself
Finding text in the widget tree is not evidence that anything rendered. WidgetTester.pump(duration) advances the clock and then builds, so a single pump after a state change captures the exact frame an animation begins on: for a result line that fades in, fully transparent, while an assertion on its text passes. The fix is two pumps, one to flush the state change and one to advance the fade; the fault was in the test rather than the app, and the reason is written beside the fix so the next screenshot test does not relearn it.
The screenshots caught a design defect before they caught a regression
Reading the rendered goldens showed the countdown ring and the target marker's emphasis halo sitting at nearly the same radius and muddying each other, so the ring moved out to r=27 with the halo pinned at radius + 9. The wrong-answer tag became an outline rather than a filled block: a solid field of the one chromatic token is more saturation than the signal needs on a monochrome canvas, and the border carries it just as well. Both reasons are written into the painter next to the values, because neither is the kind of thing an assertion would have caught.