Live full-stack product

Crossword Notes

An independent full-stack study tool that turns raw notes into interactive crossword puzzles through deterministic extraction, heuristic answer selection, constraint-based grid construction, and an AI-assisted clue pipeline with validation and fallback logic.

Turns pasted notes into playable crossword puzzles
Uses deterministic extraction plus heuristic filtering before generation
Validates and repairs AI-generated clues instead of trusting raw model output

Live at crosswordnotes.com.

Implementation highlights

Note ingestion and answer selection

The backend starts with deterministic extraction so the same notes produce a stable candidate pool. From there, I filter duplicates, rank terms heuristically, and keep the answer list focused on entries that are more likely to produce a readable board.

Constraint-based grid construction

Puzzle generation is guided by structure rather than randomness. The layout logic favors square boards, strong intersections, single-component connectivity, and general compactness so the output feels like a crossword instead of a loose word scatter.

Clue generation with safeguards

The clue pipeline is AI-assisted, but it is not unsupervised. I built validation, repair, duplicate handling, and fallback behavior into the flow so the system can reject weak output and keep the final puzzle consistent.

Project gallery

Screens, outputs, and working views

Crossword Notes input screen with note entry, crossword area, and clue panel.

Note intake and generation workspace

The main input screen where notes are pasted, answer count is adjusted, and crossword generation begins.

Crossword Notes generated puzzle with across clue list and filled grid.

Generated puzzle and clue view

A completed puzzle with the generated grid in the center and the clue list rendered alongside it.

Overview

I built Crossword Notes because I wanted study material to become something interactive without giving up quality. The project is strongest when it is treated as both a product problem and a generation problem: the user needs a fast, clean experience, and the underlying system has to build puzzles that are actually worth solving.

At a high level, the app takes pasted notes, extracts candidate answers deterministically, ranks and filters them, generates a crossword under real constraints, and then sends the filled board through a clue pipeline before the final puzzle reaches the browser.

That combination of frontend clarity and backend structure is what makes the project a flagship piece for me. It reflects the kind of work I enjoy most: software where the visible experience depends on careful implementation underneath.

Problem / Goal

Study notes are messy input. Important terms can be phrased inconsistently, the same idea can appear multiple ways, and the longest or most obvious strings are not always the best crossword entries.

Crossword quality is hard because several goals compete with each other at once. A board can be valid but still feel awkward if it is too sparse, disconnected, badly shaped, or built around weak answers.

The clue side is its own reliability problem. AI can help with clue generation, but only if the application is willing to validate the output, repair weak results, and fall back when the first answer is not good enough.

Approach / Architecture

I split the system into a clear frontend and backend workflow. The Next.js frontend handles note submission, generation controls, and the playable puzzle view, while the FastAPI backend handles note processing, generation logic, and clue orchestration.

The backend begins with deterministic extraction and normalization so the candidate set is stable and debuggable. That makes it easier to reason about why a given puzzle looks the way it does and where improvements should happen.

After extraction, the pipeline ranks and filters candidate answers before the crossword builder attempts placement. That step matters because grid quality depends heavily on the answer pool it receives.

The final stage is an AI-assisted clue flow with validation, repair, duplicate handling, and fallback behavior. The model is useful, but it only works well here because it is wrapped in structure instead of being treated as the entire product.

Engineering details

The extraction step is intentionally deterministic so repeated runs on the same notes stay understandable instead of drifting unpredictably.

Heuristic ranking helps prioritize terms that are more suitable for crossword construction and de-emphasize entries that are noisy, redundant, or unlikely to place well.

The generator optimizes for square-ish boards, strong intersection density, single connected layouts, and general compactness instead of simply placing as many words as possible.

Duplicate handling matters on both the answer side and the clue side, because repeated concepts and repeated phrasing can make the final puzzle feel weak even when the layout itself is valid.

The overall design mindset included caching, batching, and postcondition-style checks so the system could stay responsive while still defending against bad intermediate output.

Challenges

The hardest part was balancing quality against speed. Better boards often require more careful selection and placement work, but the site still needs to feel quick and usable.

A weak answer list causes problems downstream, so I had to treat extraction and ranking as core product quality work rather than a preprocessing footnote.

Clue generation added another layer of uncertainty. The AI output needed to be monitored, not just accepted, which pushed me toward repair and fallback logic early.

Because the product is live, consistency mattered. It was important that the same inputs generally behaved the same way so improvements to the pipeline were visible and testable.

What I learned

This project reinforced how much product quality can depend on algorithm design decisions the user never sees directly.

It also made me more careful about wrapping AI features in structure, guardrails, and postcondition checks instead of assuming the first output is usable.

More than anything, it showed me how satisfying it is to work on a product where UX, backend logic, and algorithmic quality all matter at the same time.