The Unity port of Echo Ridge is code-complete. Every one of the original game's 77 JavaScript modules now has C# that exists for it, checked by an audit script that exits non-zero if even one does not. The single deliberate gap is the audio folder, which the browser build never imports either.
Finished does not mean identical. The port is measured against the browser original, shot by shot, and that measurement has taught more than the porting did.
How a match is measured
Both versions render the same 642x362 art buffer, and each of 78 reference shots is compared pixel by pixel. The gate is 99% of pixels within 2/255 per channel, with no structural difference. Shots are aligned first, and the browser's own HTML overlays, such as the interaction prompt and the clock, are masked out because the port draws those differently by design.
The current mean is 59.6%. That is far from the gate, and the history behind it is more interesting than the figure.
| round | mean | shots | what happened |
|---|---|---|---|
| 2 | 42.860% | 57 | first aligned and masked round |
| 5 | 59.177% | 57 | fog fixed by changing the capture window |
| 8 | 60.858% | 70 | first round against a deterministic reference |
| 9 | 58.610% | 77 | the mean fell |
| 10 | 59.301% | 77 | the pause-menu scrim |
| 11 | 59.641% | 77 | per-band light quantisation |
| 12 | 59.619% | 78 | the last module ported; the last blocked shot came in |
By kind, round 12: interiors 76.8%, motion 72.4%, town 53.7%, UI 53.2%, cast shots 48.7%. Five shots clear 90%, all night interiors, the best at 94.1%.
Why the mean fell, twice
Round 9 went down because seven dialogue shots became measurable for the first time, and they score in the mid thirties. Round 12 went down by 0.022 because the 78th shot could not be rendered before, and it scores below the average. In both cases the denominator got more complete and the average dropped. Dropping the awkward shots would have bought the number back, and was the wrong fix.
The fixture that moved
For a while the reference itself was unstable. The browser carried its weather clock and the positions of the residents from one shot to the next, so what a frame contained depended on where it sat in the run. Both now reset before every shot, and the reference was recaptured as a whole set. Any number from before that point is not comparable with one after it.
A related trap: every number since round 2 is taken with alignment and masking on. Without them the score is about 15 points lower and looks like a regression, and an afternoon went into proving a regression that turned out to be the flags.
Notes that were wrong
Two of the project's own diagnoses turned out to be false, and each sent someone to the wrong code.
The pink awning. A note on the dawn shot, the worst in the set at 12.2%, said the awning under the diner sign was bright pink in the port and dark red in the browser. The dark red was the browser's own interaction prompt sitting in that exact spot, which is one of the masked regions. The port was never painting a pink awning. Where both frames actually show the awning, they agree closely. Any analysis of that shot has to apply the masks first.
The water. 10_day_water scored 36.9%, and was blamed on placement, variant, sort order or season. The prop drawers had already been proved bit-identical, which was the clue. The real cause was a shading pass painting over them afterwards. Switching that one pass off takes the shot from 36.93% to 84.72%, the largest single movement measured anywhere in the project.
A pass the original does not have
That shading pass, SunShade, is not a port. It is an invented per-object lighting pass for buildings, props, trees and vehicles, which the browser game has no counterpart to. Because the reference is captured from the browser, it cannot contain that pass, so every pixel it touches must differ.
| within 2/255 | debiased | |
|---|---|---|
with SunShade (ships) | 59.628% | 55.126% |
| without | 64.654% | 62.056% |
| difference | -5.026 | -6.930 |
It changes 41 shots, makes none of them worse, and leaves 37 untouched, since it switches off after dusk and in rain, fog and overcast, and interiors never run it. The decision was to keep it, because the Unity build is the product and the extra form on props and trees is wanted. It sits behind a High and Low graphics setting, and Low is the parity-exact mode. That gives two separate questions with two separate measurements: how the shipped game looks, and how faithful the port is.
The cost is that the parity number is no longer a pure fidelity score. A shot that sits on the affected list is not a defect until the five points are subtracted, and every round now prints a banner saying so.
Rounding like a canvas
The browser composites light onto an 8-bit canvas, so each light band is rounded as it is drawn and the frame accumulates the sum of rounded bands. The port first accumulated raw floats and rounded once at the end. Those differ by about a level wherever many faint bands overlap.
Quantising each band as the canvas does moved night town shots a lot: the night police shot gained 8.68 points, a rainy-night diner 3.09, and six more between 0.2 and 2.2. On the interiors it was worth only about a third of a point each. Emissive layers got the same treatment: +0.044 points on the mean across the 14 emissive shots, 12 better and 2 worse by 0.003 and 0.004.
One experiment of the same kind was reverted and recorded as a negative result. Quantising the tube-light fill moved 16 shots by +0.002 points on average, eight were completely unchanged, and it costs four roundings per pixel per fill. Noise, so it is out.
The five night interiors are the nearest thing to a pass. They sit at 90.4 to 94.1%, and once a single flat bias of about one level is removed, they score 97.2 to 98.8%. The fitted error is a faint veil, alpha about 0.029, with the port carrying slightly more contrast than the browser. It is the same level on all five. We have ruled out the multiply arithmetic, which agrees with the browser on all 65,536 input pairs, the multiply colour, and the order of the composite. The cause is not yet found.
The gates
| gate | result |
|---|---|
| typecheck, no Unity needed | 404 files clean |
| Unity batch compile | 849 items, 0 errors |
| scripted playthrough | 17 of 17 |
| EditMode tests | 558 of 575 |
| PlayMode test | 1 of 1 |
The 17 EditMode failures are stale test fixtures, checked against the browser rather than assumed, and not port defects.
The PlayMode gate is the one worth explaining. It is the only test that fails when the game stops being playable, and it is not part of the EditMode run. It had never been executed until the day the port was finished, and it failed twice when it was. A green 558 of 575 says nothing about whether pressing Play does anything.
A smaller gate lesson: a green build of the headless renderer says nothing about files it does not compile, so the typecheck over all 404 files is what backs a claim that something compiles.
One number from a decision
Residents walked at 90 in the port, a superseded value. The browser now uses 52, and the player is slower still, at 40 with a 1.65x run. The reason is the animation system: above its run threshold every resident played the run cycle, and the whole town jogged to the diner. The port takes the current values.
