Reading two columns properly takes exactly one hop
Readout shows you the plain text an applicant tracking system pulls out of your resume, next to your original layout, with the scrambled regions boxed. No score, no email, no upload.
Here is a two-column resume, skills down the left, experience down the right, as an applicant tracking system actually reads it. This is verbatim from the project's own test fixture:
CHARLIE KRUG
SKILLS EXPERIENCE
TypeScript Senior Engineer, Acme Corp
Go 2019 - 2024, Berlin
Kubernetes Led the platform team
Postgres Cut deploy time by 80%
"Go" is now a year of employment in Berlin. A keyword matcher reading that stream does not see a Go engineer who led a platform team. It sees noise. No amount of rewording fixes it, because the problem is the file and not the words.
Readout drops that page open. Give it a PDF or DOCX and it shows you the exact plain text a parser extracts, side by side with your rendered original, with the regions it scrambles boxed in amber. It is for the person forty applications into a search who has heard nothing back and is starting to suspect the tidy template rather than the experience. Every other checker I found trades a number out of 100 for your email address. This one hands over the raw text and the reasons.
The columns are not the bug
The thing I got wrong at first is the thing most advice gets wrong: two columns are not inherently unreadable. A PDF page is a stream of positioned text runs, and a parser walks that stream in the order the file emits it, not in the order your eye reads the page. If the file happens to write the entire left column out before starting the right one, a two-column resume parses perfectly. Same layout, same look, clean output. Whether it works comes down to what your exporter decided to do, which is invisible from the page.
So the detector does not look at the page. It measures crossings. Take the column boundary, walk the emission order, and count how many times consecutive text runs land on opposite sides of it. A file that writes the left column and then the right one crosses that boundary exactly once: at the handoff. A file that interleaves line by line, which is what produced the mess above, crosses on nearly every line. One hop is fine. Twenty hops is a warning, and both columns get boxed on your render so you can see which region did it.
Tables get the same treatment. Aligned cell grids are found from geometry, and if the cells come out down the columns instead of across the rows, the region is marked. A resume exported as an image gets a sentence telling you there is no text layer at all, rather than an empty panel and a shrug. A layout the parser reads correctly raises nothing. The point is an honest reading, not a scary count.
Why there is no score
There are hundreds of ATS vendors and each parses a little differently, so a single number implies a precision nobody has. That is the polite reason. The real one is that raw text is falsifiable and a score is not. You can read the extracted stream and check with your own eyes whether your job title survived next to the right employer. You cannot check "72/100" against anything. That unfalsifiability is exactly what makes a score good lead-gen and bad information.
The privacy claim works the same way. pdf.js and mammoth.js run in your tab, so there is no upload endpoint in this product, because there is no server in this product. You do not have to take my word for it: open the network tab and watch nothing fire, or just turn the wifi off and drop the file in anyway. It still works. That is a faster check than reading a privacy policy.
Honest limits, since I would rather say them here than let you find them. Floating text boxes, which are their own reliable way to get text dropped, are detected in a later version and not this one. Sub-page image regions for PDFs are not in yet either. And the warnings are heuristics, not a simulation of any particular vendor's parser. What keeps them useful is that each one is measured against a real fixture document during QA, an actual two-column resume and an actual table, rather than a synthetic string that was built to trip the check.
Try it
Open Readout and drop in the resume you have been sending out. Within a couple of seconds you get your page on one side and the parser's text stream on the other. Read that stream once, slowly, the way a keyword matcher would. If a warning fires, click it and the render jumps to the region that caused it. The source is on GitHub.
This post is part of the build log: every app my automated factory ships gets written up here, honestly. Browse everything at apps.charliekrug.com. Comments are open below.
Loading comments…