Building OnlinePuzzle.net taught me that lightweight web apps can still require heavyweight engineering — especially when content, world-building, and user experience all need to align.
When I started building OnlinePuzzle.net, my goal wasn’t just to make another puzzle game.
I wanted to create a fully engineered detective world — one that feels like stepping into a 1940s case file, but runs smoothly in the browser, loads instantly, and requires zero onboarding.
To achieve that, I had to design:
- four independent puzzle engines,
- a 365-day content system with zero repetition,
- a verifiable data pipeline,
- a consistent Noir user experience,
- and a lightweight architecture that doesn’t depend on a backend.
This post breaks down the engineering behind it.
01 — Why the Architecture Matters More Than the Gameplay
Each puzzle type is simple on the surface:
- Daily 5 — Wordle-style deduction
- Scramble — an anagram solver
- Word Search — themed 8-word grids
- Memory Clues — matching pairs of story fragments
But the vision required:
- 365 unique daily cases
- 2700+ handcrafted clues and words
- a world that feels internally consistent
- no accidental reuse or cross-leaks between puzzle types
This meant content engineering was the real challenge — not JavaScript or UI.
So the architecture had to treat the content as code, not as loose text files.
02 — TypeScript as the Content Backbone
Instead of storing text in JSON, Google Sheets, or Markdown, I structured every piece of game content as typed objects:
export interface DailyClue {
word: string
hintPrimary: string
hintSecondary: string
}
export interface WordSearchPack {
id: number
theme: string
words: string[]
}
export interface MemoryPair {
clueA: string
clueB: string
category: 'evidence' | 'person' | 'location' | 'action'
}
Why?
- Compile-time validation
- Script-based consistency checks
- Zero runtime surprises
Easy future expansion
Most importantly, I could write automation scripts that verified:no duplicates across all 2700 entries
no hidden collisions (e.g., similar stems)
all words conform to Noir style guidelines
themes and categories remain consistent
Content became first-class engineering, not decoration.
03 — Four Puzzle Engines, One Reusable Framework
The gameplay engines are fully decoupled from the UI and content.
Each engine shares:
- a unified interface
- a timing & streak module
- animation hooks
- error handling
- accessibility helpers
Example: The Daily 5 checker is a pure function:
export function evaluateGuess(guess: string, answer: string) {
return guess.split('').map((char, i) => {
if (char === answer[i]) return 'correct'
if (answer.includes(char)) return 'present'
return 'absent'
})
}
The same purity principle applies to:
- Scramble shuffling
- Word Search grid generation
- Memory Clues pairing logic The engines remain small, testable, and replaceable.
04 — The Noir UX Layer: Lightweight but Consistent
To create an aesthetic without heavy assets, I relied on:
- Tailwind CSS for the typography and paper-like textures
- Framer Motion for stamps, card flips, and scene transitions
- Web Audio API for typewriter clicks and ambient noise
- component-level variants for detective-style cards, folders, and case files
A typical Noir card component looks like:
<Card variant="case-file">
<Stamp type="classified" />
<p className="typewriter">{text}</p>
</Card>
Everything is stylized through composition, not images.
This keeps the app:
- fast
- responsive
- PWA-friendly
- easy to theme
The Noir experience is produced through design systems, not heavy graphics.
05 — PWA + Offline = A Lightweight “Daily Habit” App
The entire platform is offline-capable via Service Workers:
- assets cached
- gameplay logic self-contained
- user progress stored in localStorage
This enhances:
- load speed
- daily retention
- global access (important for puzzle players worldwide)
A puzzle game shouldn’t require a backend to function, so it doesn’t.
06 — The Detective Profile System (Without a Backend)
User data — streaks, XP, achievements — is stored locally:
interface DetectiveProfile {
streak: number
bestStreak: number
xp: number
achievements: string[]
}
Why not sync to a server?
- privacy
- instant writes
- offline mode
- reduced complexity
- no login friction
This aligns with the philosophy:
maximum immersion, minimum friction.
07 — Lessons Learned
✔ 1. Content requires the same rigor as backend code.
Unstructured text becomes a liability at scale.
✔ 2. Aesthetic consistency is a system, not a skin.
Noir experience = typography + motion + sound + narrative tone.
✔ 3. Puzzle engines must be pure functions.
It guarantees testability and portability.
✔ 4. PWAs shine when the app is revisited daily.
Daily puzzles + offline capability is a perfect match.
✔ 5. Lightweight tools can still deliver deep experiences.
People don’t need AAA graphics —
they need cohesion, polish, and emotional texture.
08 — Try the Platform
(You can add the link yourself on dev.to — the platform encourages non-spam content.)
If you enjoy:
- designing narrative-driven systems,
- building lightweight Web apps,
- or engineering large structured content sets,
then OnlinePuzzle.net might give you some inspiration.

Top comments (0)