Web App · Apps Team

Memory Game

A polished memory card game built as a learning project in the Apps Team: a Next.js / React front-end in TypeScript, wired to a NestJS backend, complete with a live timer, a leaderboard, light/dark theming and a playful horror twist.

Next.js 15 React 19 TypeScript axios NestJS CSS Modules Git / GitLab CI

The task was to build a classic memory game: a 4×4 grid of 16 cards (8 unique images, each duplicated and shuffled). Players flip two cards at a time; matching pairs stay open, mismatches flip back after a short delay. When every pair is found, the player submits their time to a leaderboard. On top of the required scope I added a light/dark theme system, background music and sound effects, and a time-based “creepy” mode with an ending scene.

Hook-driven game logic

The board lives entirely in React state via useState, useEffect and useRef: flipping, match-checking, board locking and win detection.

Timer & scoring

A live timer starts on the first flip; the final time becomes the score posted to the backend and shown on a leaderboard sorted by fastest time.

Backend integration

Card images and scores are fetched from and posted to a NestJS API via axios, with the base URL read from an environment variable.

Light / dark theming

The UI reacts to the OS colour preference, swapping card art, backgrounds and music for each mode.

Sound & atmosphere

Background tracks, match/win sounds and a timed “Sonic.exe / Mario.exe” twist with a video ending scene.

Clean config & CI

Secrets kept out of code through NEXT_PUBLIC_* env vars and GitLab CI with Secret Detection.

  • Scaffolding: created an empty NestJS app and a hover-animated card grid with the title.
  • Wiring: connected front-end and back-end, then added the click / flip handling.
  • Gameplay: built background cards, a working timer and the match logic.
  • Leaderboard: linked the score to the backend and rendered a sorted ranking.
  • Polish: finished the game, extracted the backend URL into an environment variable.
  • Atmosphere: added themed sounds and the ending scene; resolved a merge conflict along the way.

Daily commits and feature branches kept the whole progress traceable.

This project sharpened my ability to model non-trivial UI state in React with hooks, keep components typed in TypeScript, consume a REST backend cleanly, and externalise configuration through environment variables. It also reinforced disciplined version control: small, descriptive commits and resolving conflicts on a feature branch.