SimuLook · Notes from the camera

Highlight and shadow move in half steps, and we were rounding them away

Set highlight tone on a current Fujifilm and the dial does not go −1, 0, +1. It goes −1, −0.5, 0, +0.5, +1. Same for shadow tone. Those two controls, alone among the tone settings, move in half steps.

Our app stored them as whole numbers. So did every path downstream of that.

What that actually cost

Three of the seven slots on the camera we calibrate against are set to −0.5. Read one of those slots into the app and the value was rounded on the way in — before it was saved, before it was published, before it was written back.

The consequences stack up in a way that is worse than it first sounds:

  • A recipe you read off your camera was not the recipe that was in your camera. It was within half a stop of it.
  • Publish it and everyone who saved it got the rounded version.
  • Write it back to a slot and the camera ended up holding a value it had never held before.
  • Match it against the photograph it was shot with and the app would report an exact match, because both sides had been rounded into agreement.

That last one is the worst. The whole point of matching a photo against your library is to name the difference. Rounding turns a real difference into a false confirmation.

The comment was already there

The rounding was not an oversight nobody noticed. There was a comment sitting directly above it that said the model stored tone as an integer, that current bodies offer half steps, and that this was the app’s limitation rather than the camera’s.

Someone wrote that down, correctly, and moved on. It stayed true for months.

We mention it because the lesson is not “check your types”. It is that a known limitation written in a comment is not a fixed limitation, and a codebase will happily carry an accurate description of its own bug for as long as you let it.

The part that made the fix delicate

The obvious repair is to change the field to a decimal and write −0.5 to the server. That would have broken the app for everyone who had not updated.

The shipped versions decode that field as an integer. Handed a fraction they do not degrade — they throw. And because a page of the community feed is decoded as one list, a single half-step recipe would have emptied Discover for every user still on the older build. One recipe, everyone’s feed.

So the value travels twice. The old field still carries the rounded number those clients already read, unchanged. A second field carries the exact value in the camera’s own unit — the body stores tone in tenths, so −0.5 is −5, which is an integer and sidesteps the question of how a decimal survives a round trip.

Two tests hold that promise rather than a comment: one decodes what the current app writes into a structure shaped like the old contract and checks it still reads a whole number; the other round-trips every field, so a property left out of the hand-written encoder fails the suite instead of quietly dropping one of your settings.

Everything downstream had to follow

A value is only preserved if nothing along the path rounds it again.

Both editors now step by half. A whole-step editor would have re-rounded the recipe the moment its owner opened it — the same loss, one boundary further on. The camera write path sends the exact tenths, so writing a slot you read from the same body now restores what was actually there. The read-back check keeps its tolerance for floating-point noise without forgiving a real half-stop gap. And the photo matcher reports −0.5 against 0 as a difference, and names it.

Why write this down

Because the app’s pitch is that it tells you the truth about your settings, and for months it was quietly telling you something half a stop off.

The fix is in. The six recipes we published were corrected to their exact values. But an app that claims to be precise has to be able to say when it was not, or the claim is just marketing.


Written while building SimuLook, an app for reading and writing Fujifilm film simulation recipes. See which cameras are supported · Read the other notes