Here's how Code Treadmill is built and deployed. We're very open to contributions, and hopefully this documentation will help you setup your own development and testing environment.
The frontend and API are a single Next.js application. Pages are in pages/; API routes live in pages/api/. Styling is done with styled-components. Theme data is loaded at runtime from the themes/ directory and applied via a React context (AppContext).
The treadmill belt animation and RPM readout are rendered on the client only. Exercise content is loaded dynamically via import() to keep the initial bundle small.
The initial version of Code Treadmill only supported JavaScript and ran entirely in the client's browser. To add more languages, we switched to Judge0, a popular open-source code execution sandbox. It runs student submissions in isolated containers and returns stdout/stderr and exit status.
Judge0 is configured via judge0.conf and runs as a separate Docker service alongside the Next.js app. See docker-compose.yml for the full service definition.
Read exercises are pre-written, but they are written as templates that are filled with randomly-generated values (to make sure that students don't just memorize answers). Because the values are random, we must run the code to find the correct answer. Running code through Jude0 every time a new code problem loads can be too slow and puts load on the sever, so we maintain a cache of pre-computed problems.
Implemented in utils/evalCache.js. The cache lives in memory on the server, so every time the server restarts the entire cache is reset.
To tell the server which versions to avoid repeating, the browser tracks the filled code the student has already seen for each exercise. These are stored in localStorage under the key seen:<exerciseId> as a JSON array. The array is sent with each eval request so the server can select an unseen version from the pool.
Every time a student tries to load a problem, the server first checks to see if a version they haven't already seen is available in the cache. If so, a random version that the student has not already seen is returned. If all pooled versions have been seen a new version is generated and added to the pool. If the template has no fill markers (every fill is identical), any pooled version is returned.
A POST or GET to /api/warmgenerates the cache for every exercise in the library. Progress and current cache state are available at /api/cache-status.
Multiplayer races use a WebSocket server defined in pages/api/socket.js. The race manager and all participants connect to the same socket room keyed by race ID. Score updates, participant joins, and start/stop events are broadcast to all members of the room in real time.
The production server runs via Docker Compose, which runs the Next.js app and Judge0 together, and sets up connections between them. Note that Jude0 contains several docker containers.
To start the production stack:
docker compose upNote that this copies the entire repo into the container and builds there. If you modify the code and want to update the container, run:
docker compose up --buildFor development, you'll also want to rundev.sh alongside the production Docker containers. This development version uses the Next.js development server which connects to the Judge0 instance in docker and includes live refreshes (to avoid having to rebuild the production Docker container on every update). After running dev.sh, the testing server will be available at locahost:3001.
On startup, the testing server also Regenerates any stale or missing HTML/CSS exercise screenshots usingPlaywright.
| Path | Contents |
|---|---|
pages/ | Next.js page routes and API routes. |
components/ | React components shared across pages. |
exercises/ | Exercise bank files — one file per topic, keyed by exercise ID. |
workouts/ | Workout definition files that reference exercise IDs. |
themes/ | Colour theme files (one per theme) plus themeOptions.js. |
utils/ | Shared utility functions (scoring, code evaluation, theme helpers, etc.). |
public/ | Static assets including pre-generated HTML/CSS target screenshots. |
scripts/ | Build-time scripts, including the Playwright screenshot generator. |
vendor/ | Vendored libraries (e.g. 8080js CPU emulator). |