Full-stack web application using Scala 3, Scala.js, Cask, and WebSockets.
Counter App with Real-Time Sync
- Simple shared counter that syncs across all browser tabs
- Click to increment or decrement
- Watch updates appear instantly everywhere
- Demonstrates real-time WebSocket synchronization
Color Rush - Multiplayer Game
- Fast-paced color matching challenge
- Compete against other players in real-time
- 10 rounds with speed bonuses
- Live scoreboard updates as you play
AI Drawing Challenge - Multiplayer Game
- Draw prompts and let AI caption your artwork
- OpenAI Vision API analyzes drawings
- AI judge picks the best match with witty commentary
- Players vote for their favorite drawing
- Real-time lobby system with WebSocket sync
- Bring your own OpenAI API key
Terminal 1 - Transpile client (watch mode):
sbt ~"js/fastLinkJS"Terminal 2 - Start server:
sbt "jvm/run"Browser:
http://localhost:8080
Open multiple tabs and watch them sync!
Compile:
sbt compileClean compile:
sbt clean compileRun tests:
sbt "jvm/test"Start server:
sbt "jvm/run"Transpile client (dev):
sbt "js/fastLinkJS"Transpile client (watch):
sbt ~"js/fastLinkJS"Transpile client (production):
sbt "js/fullLinkJS"Generate coverage report:
ENABLE_COVERAGE=true sbt clean coverage test coverageReportCount lines of code:
./scripts/count-loc.shLanding Page:
GET /→ Landing page with overview of available apps
Counter Demo:
GET /counter→ Counter app (HTML client)GET /api/counter→ Get counter value (JSON)POST /api/counter/increment→ Increment counterPOST /api/counter/decrement→ Decrement counterWS /ws/counter→ WebSocket for real-time updates
Color Rush Game:
GET /color-rush→ Color Rush game clientWS /ws/color-rush/:gameId→ Game WebSocket endpoint
AI Drawing Challenge:
GET /drawing→ AI Drawing Challenge game clientWS /ws/drawing/:lobbyId→ Lobby WebSocket endpoint
Static Assets:
GET /static/*→ Static files (HTML, CSS, JS)/static/js/main.js→ Compiled Scala.js (includes all apps)/static/index.html,/static/counter.html,/static/color-rush.html,/static/drawing-game.html→ HTML files/static/base.css,/static/counter.css,/static/color-rush.css,/static/drawing-game.css→ Stylesheets
System:
GET /health→ Server health and statistics (JSON)- Status, uptime, memory usage, system info
Stack:
- ✅ Scala 3 for both server and client
- ✅ Cask for lightweight server framework
- ✅ Scala.js for client-side code sharing
- ✅ WebSockets for real-time communication
- ✅ Cache-busting system for static assets
- ✅ currently solved via client-side hack (manually append server version as query param on static asset URLs)
- ⏳ build proper solution with hashed filenames and manifest mapping
- ⏳ Database persistence, ORM (PostgreSQL + Doobie?)
- ⏳ DB migration system (Flyway, Liquibase?)
- ⏳ Cache across instances / across restarts (Redis?)
- ⏳ Internationalization (German, English)
Testing:
- ✅ ScalaTest
- ✅ jvm: works out of the box
- ✅ js: requires jsdom plugin
- ✅ cross-project setup to cross-compile shared sources
- ✅ isolated endpoint tests (BDD)
- ✅ full endpoint traversal (
requests.get(path)) with coverage - ✅ support for mocking side effects
- ✅ full endpoint traversal (
- ✅ WebSocket tests
- ⏳ e2e tests
- ⏳ Selenium, Cypress, Katalon? To be decided.
- ⏳ CI integration (headless)
Continuous Integration:
- ✅ CI pipeline running tests
- ✅ Test coverage report (sbt-scoverage)
- ❌ unfortunately, ScalaJS is not supported by scoverage, so we cover only the jvm target
- ✅ Lines of Code tracking (auto-generated on each CI run)
- ✅ Cloud deployment (railway), deploy on push
- main → staging environment
- production → production environment
Monitoring:
- ✅ health endpoint
- ⏳ endpoint for inspecting number of active games and websockets (with age)
- ⏳ Exception tracing (Sentry, Datadog, BetterStack?)
- ⏳ Log aggregation (Logstash, Datadog, BetterStack?)
- ⏳ Health and Performance monitoring
- Missing tests for Drawing game
- ✅ WebSocket closes on its own after some time of inactivity (1-2 minutes)
- fixed by implementing a heartbeat mechanism: client sends ping every N seconds
- ✅ WebSocket connection is lost on page refresh
- fixed by storing player id in localStorage and rejoining the game/lobby on page load if player id is found
- Health Endpoint - Detailed documentation of the
/healthendpoint and its stats - Game Cleanup - Documentation of automatic game/lobby cleanup mechanisms