Skip to content
All work
University · Sep 2023 – Jan 2024
Trailing.io logo

Trailing.io

Coding course platform that grades HTML/CSS by comparing screenshots

Role
Developer in a team of 5
When
Sep 2023 – Jan 2024
Status
Generated real revenue
Highlight
~26K lines of code I wrote
0:00 / 1:14

Overview

What it is

Trailing.io teaches web development and Python through short exercises solved in the browser. A team of five built it during the Minor Entrepreneurship, from September 2023 to January 2024: five months of full-time development. It generated revenue from paying users and placed 2nd out of 23 teams. Learners follow courses laid out as stages of exercises, get AI hints, and earn bytes, a virtual currency they spend in a shop.

I wrote about 26K lines of its code. My focus was the core: the screenshot-diff grader for HTML/CSS exercises, and a full course-building admin panel. My teammates used that panel to create three complete courses: HTML & CSS, Python and JavaScript.

The grader needs no hand-written tests. An exercise author supplies a target page and a required similarity, and the server renders both pages in headless Chrome and compares the images.

Why it’s interesting

  • Both pages are rendered in headless Chrome with the viewport sized to the body, so the screenshot covers the whole page, not just the first screen.
  • If the two screenshots differ in size, the grader returns a fail without comparing pixels. Otherwise pixelmatch scores them at threshold 0.1.
  • Every img tag is swapped for one fixed placeholder before rendering, so remote images cannot make two identical pages differ.
  • A required-words pre-check counts each word against the starter code, so a page that only looks right by accident is rejected before rendering.
  • A Judge0 container runs Python, JavaScript and C submissions in a sandbox, next to the visual grader for HTML/CSS.
Revenue
from real paying users
5 months
of full-time development
Placed 2nd
out of 23 teams
~26K
lines of code I wrote

git blame

Architecture

How it fits together

Stripecheckout, webhookWeb appReact, ViteSocket.ioAI hintsAPI serverExpress, TypeScriptOpenAIGPT-3.5Graderin-process PuppeteerPostgresPrisma, 29 modelsJudge0Python, JS, CDaily jobnode-cronRESTwebhookrender twicerun codestream hinthint chunkshint chunksdaily reward
The browser calls the Express API, which renders each HTML submission twice in the grader and sends other languages to Judge0, while Socket.io pushes AI hint chunks back and a cron job writes the daily reward. data external sync call async / webhookhover a box to trace it

Try it

Get graded by the pixel-diff grader

A browser re-creation of the grader's rules: change the student's CSS and a pixelmatch-style colour comparison (threshold 0.1) scores it against the target. As in the real grader, a page of a different size fails without any pixel comparison.

Screens

A closer look

Course catalogue. Course cards with a progress bar on the one in progress, free and paid courses together.

1 / 9

Tap a screenshot to enlarge it.

Features

Everything it does

Learning

  • Courses and roadmaps — Courses are stages of levels; a level is a code exercise or a multiple-choice question.
  • Browser editor — Monaco with HTML, CSS and JS tabs, live preview, a Correct output toggle and optional Vim mode.
  • Four languages — Web development graded visually, plus Python, JavaScript and C run in a sandbox.
  • Practice topics — Mini exercises by topic, graded by the same screenshot comparison.
  • Help along the way — Solution after two failed attempts, cheat sheets, in-app tutorial and lesson feedback.

Visual grader

  • Screenshot comparison — Student and target pages rendered in headless Chrome and scored with pixelmatch.
  • Required similarity — Each exercise sets its own percentage; the demo exercises use 98 or 99.
  • Required words — A pre-check rejects submissions that skip the HTML or CSS the exercise asks for.
  • Image placeholder — Every img source is replaced by one fixed placeholder before rendering.
  • Rate limit — Submissions are rate-limited with express-rate-limit.

AI

  • Get a hint — GPT-3.5 streams a hint over Socket.io; the prompt forbids giving the solution.
  • Code Assistant — A chat helper on each exercise, using the same streaming path.
  • Problem generator — Admins can draft exercises with an OpenAI-assisted generator.

Gamification

  • Bytes — A virtual currency awarded for first-time solves, the daily reward and the daily challenge.
  • Shop — Avatars bought with bytes; byte packs bought with Stripe.
  • Streaks and leaderboards — Leaderboards by total bytes earned or current streak.
  • Profile — Progress rings per course, an activity chart, followers and referrals.
  • Certificates — A PDF is drawn onto a template with pdf-lib when a course is finished.

Accounts and admin

  • Sign-in — Email and password, Google and GitHub OAuth, email verification and password reset.
  • Paid courses — Stripe checkout and a webhook, with a payment history page.
  • Admin CMS — Exercises, courses, blog, categories, users, bug reports and multiple-choice questions.
  • Blog — Public blog with an index and detail pages.

Tech stack

What it's built with, and why

Frontend

React 18 + TypeScript + Vite
Single-page app with 52 page components.
Redux Toolkit
Holds user, roadmap and level state, the shop and the selected language.
Monaco
In-browser editor for exercises, with an optional Vim mode.
Tailwind CSS
Styling, with Headless UI, framer-motion and ApexCharts for the activity chart.

Backend

Express + TypeScript
REST API with JWT in an httpOnly cookie, refresh tokens and rate limiting.
Socket.io
Streams AI hints and like counts to the browser.
Puppeteer + pixelmatch + cheerio
Render both pages, rewrite img tags, and score the screenshots.
pdf-lib + node-cron
Course certificates and the daily reward and challenge job.

Data

PostgreSQL + Prisma
29 models and 78 migrations: courses, levels, submissions, rewards, payments.

AI

OpenAI (GPT-3.5-turbo)
Streamed hints, the chat assistant and the admin problem generator.

Integrations

Stripe
Checkout for paid courses and byte packs, with a webhook.
Mailjet
Verification and password reset emails.
Google and GitHub OAuth
Alternative sign-in.
Judge0
Sandboxed execution of Python, JavaScript and C.

Infra and tooling

Docker Compose
Local Postgres, and a separate Judge0 stack.
GitHub Actions
Backend and frontend checks on pull requests.
ESLint + Prettier
Shared code style across the team of five.