How we store your results (and what we don't keep)
Every result is now saved to your account. Here is exactly what that record contains, what it doesn't, who can read it, how to delete it, and what analytics sees.
An earlier version of this post was called Why we don't store your test results. It promised that if that ever changed, we would say so here, with the new effective date on the privacy policy, and that the change would apply forward only. This is that post.
Taking the test now needs an account, and every completed test is saved to it. The first test on an account is free; unlimited testing is $20, once. The reason is the one the old post listed under "what we give up": a single contrast sensitivity result is a snapshot, and the useful thing is the trend. A trend needs a record that survives a cleared browser, a new phone, or a laptop that went back to work. So the record now lives with your account, and this post is the exact shape of it.
What is stored
When a test finishes, the test app writes one result object and sends it to your account. The record is small — about a kilobyte — and it contains:
- The mode — quick (one spatial frequency) or full (five).
- The curve. At each spatial frequency tested: the threshold contrast, the log contrast sensitivity derived from it, and how many trials the staircase used to get there, with the counts of "not sure" answers and timed-out trials at that frequency.
- The AULCSF summary — the area under the log-CSF curve — for full-curve tests.
- The calibration values. Pixel pitch in millimetres, viewing distance in millimetres, and the display gamma the stripe-match step estimated, along with how each was measured (a dragged credit-card outline or a known-device pick; the blind-spot method or a typed-in distance). These are what make one result comparable with the next.
- Timestamps — when the test finished on your device, and when the server stored it.
- A dropped-frequency list, if your display couldn't resolve the highest frequencies at that viewing distance.
The server adds an id and files the record under your account id in a private store on Vercel (Vercel Blob). The account also keeps an index — one row per result, with the AULCSF and the curve points, so the history page can draw the trend without opening every file — and a lifetime count of tests taken, which is how the free first test is counted.
What is not stored
- Your individual per-trial answers. The staircase's trial-by-trial record never leaves the test screen; only the counts above survive it.
- The stimulus images. They are generated on the fly on your screen and discarded.
- Anything about your device beyond the calibration numbers. No user agent, no screen model, no fingerprint. Pixel pitch is the closest thing to a device description in the record, and it is there because the measurement needs it.
- IP addresses. The results store holds what the test app sent plus server timestamps. Our hosting provider keeps standard short-term access logs for security and uptime, as every host does; those are separate from your results and are not joined to them.
- Your name, address, or phone number. The test doesn't ask. An account is an email address to sign in with, handled by Clerk; we file results under the account id Clerk gives us.
- Card numbers. Stripe handles payment; we receive an indicator that it succeeded.
Who can read it
Only you, signed in. A stored result is served only to the account that owns it, and an id that isn't yours returns the same "not found" as an id that never existed, so results can't be probed. There is no results dashboard in the product and no view that lists results across accounts. We can reach the raw files as the operator of the store, the way any host can; that access is for keeping the service running, not for reading results.
The two ways a result reaches anyone else are both yours to trigger, and both are described below.
The copy on your device
The test app writes every result to your browser's localStorage first, then uploads it. That order is deliberate: a test taken on a train still finishes, and past results open offline. The local copy is the same record as the stored one, plus a note of whether the upload succeeded.
If a result couldn't be uploaded — no network, a rate limit — it stays local and the app retries the next time it opens with you signed in. Results you took before you had an account, from the days when the browser was the only place they lived, are uploaded the same way, and the server keeps them outside the free-test count, since they predate it.
Deleting
Every result has a Delete button in the test app's results list. Pressing it removes the file from your account, removes the row from the index, and removes the local copy from that browser. It is a real delete, not a hidden flag.
Two honest caveats. First, the lifetime count of tests taken does not go back down — deleting your one free result does not hand the free test back. Second, a copy of a result that you shared by email, PDF, or link has left our systems and isn't ours to delete.
To delete the whole account, email privacy@contrastscreen.com from the address on the account. We remove the account and every result stored against it within 30 days, except where a Stripe-related financial record has to be kept.
There is no automatic expiry. Results stay until you delete them or the account, because the trend is the point.
Sharing
A result link. The test app's "Share" builds a URL with the result encoded after the #. Browsers separate the fragment from the rest of a URL before making a request and never send it to the server,1 so opening a result link doesn't report back to us. Whoever holds the link can read that one result — the same trust model as forwarding a PDF. Result links are part of unlimited.
Email my results. Free on every account. The test app sends that one result to our server to be rendered and emailed to your own account address. The server keeps neither the email nor the recipient.
Share with a provider. Part of unlimited. From a result, it emails that result and a note to any address you type, with replies going to you. From the history page, it emails the whole list and the AULCSF trend along with a read-only link to the charts that works for 30 days and needs no account. The provider's address is used for that one email and not kept. Anyone holding the history link can view your history until it expires; sharing again mints a fresh link, and there is no way to revoke one early, so treat it like the PDF.
Analytics
Product analytics is on by default. We use PostHog on both the marketing site and the test app to count events: test started, calibration complete, staircase complete (with the frequency, the threshold contrast, the log CS, and the trial counts), test complete, result viewed, result shared. On the marketing site only, Microsoft Clarity records anonymized, IP-masked session replays to see which parts of a page confuse people. Clarity is never loaded during the test; the stimulus screen runs without third-party scripts.
The events carry the psychophysical numbers and a first-party cookie that lets a "test started" and a "test complete" from the same browser count as one visit. They don't carry your name, your email, or your per-trial answers, and analytics is not how a result reaches your account — that is the results sync above.
If your browser sends Do Not Track, analytics stays off on both apps without you doing anything. Otherwise the Manage analytics link in the test app's footer switches it off, and the choice sticks.
Why the record stays small
The reasoning from the old post still holds; it just applies to a smaller pile now. Reduced contrast sensitivity is associated with cataract, glaucoma, multiple sclerosis, concussion, certain medications, and normal aging. None of those associations is diagnostic, and the score on its own hands nobody to anybody. The linkage would — a low score next to a name, a date of birth, a postcode.
Re-identification of "anonymous" health data is hard to keep solved at scale. Latanya Sweeney showed in 2000 that ZIP code, sex, and date of birth together uniquely identify roughly 87 percent of the US population,2 and in 2013 attached real names to 43 percent of newsworthy patient records in a $50 state hospital-data release by cross-referencing news reports.3 The lesson we take from it is about what to keep, not just how to guard it: the fewer identity attributes sit next to a score, the less there is to link. So the record is the curve, the calibration, and the timestamps, filed under an opaque account id — and nothing we don't need for the measurement or for showing you the trend.
We will not sell or share any user data. That commitment is unchanged from the old post and is written into the privacy page.
What changed, and what didn't
Changed: results are saved to your account, an account is required, and there is a paid tier. The trade-offs the old post said we were eating — no history across devices, no automatic re-test comparison — are the ones we stopped eating.
Unchanged: the test still computes your result in your browser. The per-trial record still never leaves the screen. There is still no data broker, no ad network, and no cross-site tracking. Share links still use the fragment so that opening one doesn't report back. And the privacy policy is still the binding document; if anything here drifts from it, the policy wins.
Take the test
Take the test with a free account. The first one is free; unlimited is $20, once. Your results are at contrastscreen.com/results whenever you want them, and you can delete any of them whenever you don't.
For the longer story of why the test exists, the origin post covers it. For the legal version of this policy, the privacy page is the authoritative source.
Note. This post describes how results are handled as of September 2026. The privacy policy on this site is the binding document; if anything here ever drifts from it, the policy wins.
Footnotes
-
Berners-Lee T, Fielding R, Masinter L. Uniform Resource Identifier (URI): Generic Syntax. RFC 3986, §3.5 (Fragment), 2005. "The fragment identifier is separated from the rest of the URI prior to a dereference, and thus the identifying information within the fragment itself is dereferenced solely by the user agent" — i.e. the client resolves it; it is not part of the request sent to the server. RFC Editor. ↩
-
Sweeney L. Simple Demographics Often Identify People Uniquely. Carnegie Mellon University, Data Privacy Working Paper 3, 2000. Using 1990 US Census data, about 87 percent of the US population (216 of 248 million) were likely unique on the combination of 5-digit ZIP, gender, and date of birth — the foundational result on why "anonymous" health data tends to stop being anonymous alongside a second dataset. dataprivacylab.org. ↩
-
Sweeney L. Matching Known Patients to Health Records in Washington State Data. Harvard University Data Privacy Lab, White Paper 1089-1, 2013 (arXiv:1307.1370). Washington sold patient-level hospital data for $50; cross-referencing public news reports uniquely and exactly matched 35 of 81 (43 percent) newsworthy hospitalization records to named patients. arXiv. ↩
Frequently asked questions
Yes. Since results moved to accounts, every completed test is saved to your account in a private store, and a copy stays in the browser you tested on. The record is the curve, the calibration values, and timestamps — not your per-trial answers.
Open the results list in the test app and press Delete on the result. It is removed from your account and from that browser. To delete the account itself, email privacy@contrastscreen.com from the address on the account; the account and every result stored against it are removed within 30 days.
A result link carries that one result in the part of the URL after the #, which browsers don't send to servers, so opening it doesn't report back to us. Anyone holding the link can read that result. Sharing your full history with a provider mints a read-only link that works for 30 days; anyone holding it can view your history until it expires.
Yes, by default. PostHog counts events on the marketing site and the test app — test started, calibration complete, test complete, with the resulting numbers — and Microsoft Clarity records anonymized sessions on the marketing site only. Both honor Do Not Track, and the Manage analytics link in the test app's footer switches capture off. No name, email, or per-trial answers are sent.
curious where your CSF sits?
Your first calibrated test is free, in your browser, about three minutes. Unlimited testing with saved history is $20 — once.