Inspiration

I've been deep in my own UC application essays this summer, and the hardest part isn't writing. It's knowing which lines actually work and which ones sound like every other applicant's essay. I kept wishing I had an editor sitting next to me, circling the clichés and the vague sentences in real time instead of me guessing. So I built one.

What it does

Marginalia takes your essay draft and marks it up directly, the way a sharp human editor would with a red pen. It highlights three kinds of lines right inside your own text:

  • Clichés: phrasing that shows up in a thousand other college essays ("ever since I was young," "passion for helping others")
  • Vague lines: statements that sound nice but have no specific, concrete detail behind them
  • Strong lines: sentences that are specific and vivid and should be kept as is

Each flagged line gets a short margin note explaining exactly why it was flagged, so you're not just told something is wrong. You're told what to fix.

How we built it

It's a single static HTML page, no backend, no build tools. The essay text gets sent straight from the browser to the Claude API with a system prompt that instructs it to return structured JSON: a list of annotations, each with a type (cliché, vague, or strong), the exact quoted span from the essay, and a one sentence explanation. The frontend then finds those exact quotes inside the original essay text and wraps them in colored underlines, so the feedback appears literally on top of your own writing instead of in a separate report.

Since it's a static page with no server, it uses a bring your own API key pattern. Your Anthropic key is entered client side and used only to call the API directly from your browser, never stored or sent anywhere else.

Challenges we ran into

Getting the AI to return quotes that matched the original essay text exactly was harder than expected. If the model paraphrased even slightly while quoting, my code couldn't find that string in the essay to highlight it. I had to be very explicit in the system prompt that quotes must be character for character verbatim, and add a fallback that just skips any annotation it can't locate rather than crashing.

I also had to think carefully about overlapping annotations. If two flagged spans overlapped in the text, the highlighting would break. I ended up sorting all annotations by position and simply skipping any that overlap with one already placed.

Accomplishments that we're proud of

Getting the AI feedback to render inline on the actual essay instead of as a generic separate scorecard. That was the whole point: feedback that sits exactly where the problem is, not detached from the writing. I'm also proud this is something I'll genuinely use myself on my own UC essays this fall, not just a hackathon demo that gets abandoned after judging.

What we learned

I learned a lot about prompting for structured, parseable output. Getting an LLM to reliably return valid JSON with exact match quotes took real iteration on the system prompt, not just a single instruction. I also got more comfortable calling the Claude API directly from client side JavaScript, including the specific headers needed for direct browser access, which I hadn't done before.

What's next for Marginalia

I'd like to add a running "essay health" summary at the top (rough tone, length compared to prompt limits, cliché density) in addition to the line level annotations, and eventually support comparing multiple drafts side by side to track whether revisions are actually improving the essay over time.

Built With

Share this project:

Updates

Submission history