{Word Cross} Project Report
1. The Concept
Word Cross is a cozy mobile word puzzle game: swipe letters on a circular wheel to spell words and fill a crossword-style board. The hook is the familiar Wordscapes-style loop (connect → discover → fill the grid) wrapped in a warm garden-adventure theme — 13 themed worlds (SPARK through ZENITH), coin rewards, bonus words, and star ratings per level.
The game targets quick, satisfying sessions: easy to pick up on Level 1 (with a welcome intro and swipe hint), but with 156 levels that ramp from 3-letter puzzles to 6–7 letter boards for longer play. Design followed Sakurai-style rules already in the repo: clear problem-solving loop, data-driven parameters, risk/reward (spend coins on hints vs. earn them from bonus words), and a portrait phone-first UI that feels like a game, not a website.
Difficulty is now progressive by level number — puzzles are sorted by actual complexity (letter count, word count, grid size, cross density) so World 1 is genuinely the easiest stretch and World 13 the hardest, with hint costs rising as you advance.
2. The Stack
- HTML5 — single-page shell with title, world map, play screen, overlays (intro, pause, level complete), unified top HUD, and semantic accessibility labels
- CSS3 — custom garden/cozy visual system (gradients, wood panels, vine canopy, animated stars); Fredoka + Nunito via Google Fonts; no CSS framework (~1,850 lines)
- Vanilla JavaScript — all game logic, crossword packing, progression sorting, input, audio, save, and effects in one file (~2,315 lines); no MVC, no build step, no npm
- Canvas — starfield background, swipe trail on the letter wheel, particle burst FX on word clears
- Web Audio API — procedural music (home/map themes) and SFX; HTML Audio fallback for
audio/clear.mp3on level complete - localStorage — persistent coins, stars, mute state, and “seen intro” flag (
wordcross_v2) - Pointer Events + touch — smooth swipe-to-spell with backtrack support, shuffle, and hint buttons
Architecture choices: parameters (PARAMS) separated from logic; levels defined as compact strings (LETTERS|grid,words|bonus,words); crossword layouts generated at runtime by a word-packing algorithm; level order computed at boot via buildProgression() rather than hand-sequenced.
Documentation: README.md covers features, quick start, economy, level format, audio, testing, and design doc references.
3. The 1 Day Sprint (+ polish pass)
Morning (Hours 1–3):
- Scoped the project: one HTML, one CSS, one JS — no framework, smooth mobile play, many levels
- Read existing Sakurai design docs and UI rules in the workspace to match intended game feel
- Built core screens: title, world map (12 nodes per world), and play HUD
- Implemented letter wheel with pointer drag, golden swipe trail, and word preview feedback (good / bad / bonus)
- Wrote crossword auto-packer: places words horizontally/vertically with crossing letters, scores layouts by compactness
- Authored 156 levels across 13 worlds with grid words + optional bonus vocabulary
- Added coin economy, hint (40 coins), shuffle, star rating (3 stars = no hints), and localStorage save
Afternoon (Hours 4–6):
- Fixed Level 7 — broken sparse crossword from over-packed words (
TOP|POT|OPT|TO); moved extras to bonus and tightened packer rules - Smoothed coin-fly animation (was jerky / “ញ៉ាក់”): smoother bezier arc, GPU
translate3d, lighter HUD pulse instead of shake-on-every-coin - Fixed HUD layout — coin pill was overlapping the LEVEL badge; switched play header to a 3-column grid with reserved slots
- Replaced broken CSS clip-path icons with clean SVG glyphs (mute speaker, shuffle arrows)
- Added Level 1 welcome intro overlay (“Connect the letters and discover all the words!”) with one-time
seenIntrosave flag - Iterated garden UI: hint cost badge placement, flower/garden-floor styling, title hero plaque, PLAY button sizing, MAP → LEVEL label
The Finish Line (Hour 7-8):
- Ran
node game.jsself-test to validate all 156 levels pack without missing grid words - Polished win flow: level complete panel, star pop animation, coin fly on rewards, pause/retry/level menu
- Added procedural background music for title and map screens; victory jingle with MP3 + synthesized fallback
- Differentiated pause menu button colors (RETRY green vs. LEVEL blue) so actions are visually distinct
- Final mobile tuning: safe-area insets, touch-action none, resize/orientation reflow for board and wheel
Polish (Aug 17 — morning): Extended same-day pass after the core sprint — mostly visual and feel fixes driven by playtesting screenshots:
- Title screen wooden sign redesign (kicker, logo tiles, spark accent)
- Coin pill restored to original warm wood style after an unwanted restyle
- Hint FAB: star icon + cost pill layout refinements
- Home/map BGM melody rework for warmer tone
- Level complete sound reliability fix (
audio/clear.mp3first, then Web Audio fallback) - Garden canopy, vignette, and flower dots on the title floor
Polish (Aug 17 — afternoon): Playtesting feedback drove two structural fixes:
- Unified top HUD — mute, back, pause, level badge, and coin pill were scattered across separate absolute-positioned corners and couldn’t stay vertically aligned (44px pill vs 54px button). Replaced with a single
#top-hudgrid bar (54px | 1fr | auto) shared across all screens; context buttons show/hide per screen viashowScreen(). - Progressive difficulty curve — original level order mixed easy and hard puzzles within the same world (e.g. a 7-letter HEART puzzle early, 3-letter CAT late). Added
levelWeight()scoring (letters, word count, max word length, grid area, cross count),buildProgression()to sort all 156 puzzles ascending, andhintCostFor(index)(30 → 65 coins). Map nodes color-coded by tier: gold (easy), orange (normal), red (hard). Save key bumped towordcross_v2so progression matches the new order. - README.md — full project documentation written (features, setup, level format, economy, testing checklist).
4. The Roadblocks
- Crossword packing edge cases — Some word sets (especially short overlapping letters like TOP/POT/OPT) produced sparse or disconnected grids. Fixed with
tooSparse()rejection, demoting overflow words to bonus, and auto-completing words when all their cells are revealed.
- Animation jank — Coin fly felt stuttery because each landing re-triggered a CSS shake and sparks spawned separate
requestAnimationFrameloops. Replaced with a single fly loop, smoother easing, and a one-shot HUD bump.
- Layout collisions on small portrait screens — Fixed-position coin pill collided with centered level badge. Needed explicit grid columns and a spacer instead of absolute positioning alone.
- CSS-only icons — Mute and shuffle buttons used clip-path/conic-gradient hacks that rendered off-center and ugly. SVG icons solved it cleanly without image assets.
- Audio on mobile — Browser autoplay policies and async MP3 loading meant level-complete sound sometimes never fired. Layered HTML Audio + synthesized fallback and unlock-on-first-tap.
- Scope vs. time — 156 hand-curated level strings + a custom packer in one day meant bugs surfaced only during play (e.g. Level 7), not at author time. The
selfTest()runner became essential.
- HUD alignment across screens — Tweaking
topoffsets per element (mute button, coin pill, pause button) couldn’t produce a reliable shared baseline because elements lived in different DOM contexts with different heights. A single flex/grid header was the only durable fix.
- Flat difficulty curve — Levels were authored by theme/world, not by puzzle weight. Early worlds had occasional hard puzzles and late worlds had easy ones. Sorting by computed weight at boot fixed progression without rewriting all 156 level strings.
5. Key Takeaways
- Vanilla can ship fast — For a focused mini-game, three files beat framework overhead. No build step means instant refresh while iterating UI from screenshots.
- Data-driven levels scale — One string per level (
CAT|CAT,AT|ACT) let us add 156 stages without a level editor; the packer is the real engine. - Feel is half the game — Swipe trail, coin arc, cell pop, and SFX matter as much as the word list. The most reported bugs were polish (jank, overlap, icons), not logic.
- Test the packer, not just the parser — Valid words ≠ valid crossword. Automated packing checks saved hours of manual level QA.
- Design docs upfront helped — Sakurai rules and UI text rules in the repo kept decisions consistent (portrait, one action per screen, parameter tuning without code changes).
- Mobile-first constraints are features —
touch-action: none, pointer capture, and safe-area CSS turned a desktop prototype into something that actually plays well on a phone. - Sort progression at runtime — When content is authored in batches, a weight function + sort at boot beats manually re-ordering hundreds of level strings.
- One HUD, one row — Shared chrome (coins, nav, mute) should live in one container with grid/flex alignment, not independent absolute corners.
My final thought:
Word Cross started as “make a smooth Wordscapes-like game in one day with no framework” and ended as a surprisingly complete pocket adventure — 156 levels sorted by real difficulty, a save system, music, intro, progressive hint costs, and a garden world that feels intentional. The hardest part wasn’t spelling words; it was making every swipe, coin, and button feel good on a small screen, and making sure level 130 actually feels harder than level 3. That polish loop — build, play, screenshot, fix — is what turned a logic demo into a game worth opening again.