This project is a Next.js + TypeScript app that visualizes courses across academic years and periods. It draws horizontal course bars spanning study periods, stacks course credits vertically (15 ECTS = full year band), shows prerequisite arrows, marks exams (filled circles) and re-exams (open circles), and includes visual connectors for courses spanning consecutive periods. The app supports SVG and high-quality PDF export with proper font rendering.
Prerequisites
- Node.js (>=20 recommended; CI runs on Node 20)
- npm (comes with Node)
Install and run locally:
# from the workspace folder containing this README
cd program-visualization
npm install
npm run dev
# open http://localhost:3000Other useful commands (all of these also run in CI):
npm run lint # ESLint (max-warnings 0)
npm run validate-data # Schema + cross-reference checks for src/data/*.json
npx tsc --noEmit # TypeScript diagnostics without emitting
npm run build # Production buildIf the dev server appears suspended (e.g. you see zsh: suspended npm run dev), resume with fg in the same terminal or start fresh with nohup npm run dev > /tmp/next-dev.log 2>&1 & and check logs with tail -f /tmp/next-dev.log.
Recommended: Vercel (works well with Next.js). Create a GitHub repo and connect it to Vercel. Default build command npm run build and output directory are handled by Next.js.
Important for Vercel deployment:
- The PDF export feature requires Puppeteer and a serverless-compatible Chrome binary. This is handled automatically by
@sparticuz/chromium. - The
vercel.jsonfile configures increased memory and timeout for the PDF generation endpoint. - Hobby Plan Limitation: Vercel's Hobby (free) plan has a 1024MB default memory limit with a maximum of 2048MB for serverless functions. The configuration uses 1800MB to stay within this limit. If deployments silently fail from GitHub, check that the memory allocation in
vercel.jsonis ≤2048MB. - Pro plans support up to 3008MB which may improve performance for larger visualizations.
Other hosts: Netlify or static exports (with limitations). See Next.js docs for deployment options.
Top-level (inside this folder):
package.json— project manifest and scripts (dev, build, start).tsconfig.json— TypeScript configuration.next.config.ts— Next.js configuration.
Key source files
src/app/page.tsx— main page that mounts the visualization component.src/app/HomeClient.tsx— client-side wrapper for the visualization with program selector.src/components/TimelineVisualization.tsx— the D3 + React visualization (the largest file in the project). Draws the SVG, course bars with visual connectors for consecutive periods, prerequisite arrows, exam/re-exam markers, focus-mode interactions, and handles SVG/PNG/PDF export. The option-group selection modal is inlined at the bottom of this file.src/components/Legend.tsx,InfoPanel.tsx,OptionGroupModal.tsx,SpecializationFilter.tsx,Toast.tsx— supporting UI pieces.src/app/api/export-pdf/route.ts— API endpoint for server-side PDF generation using Puppeteer and@sparticuz/chromium.src/types/course.ts— TypeScript types (Course, Period, etc.) and the exportedacademicPeriods(loaded from JSON).src/types/cosmetics.ts— TypeScript types for program-specific visual customizations (colors, positions).REVIEW.md— open issues, design discussion, and the ranked improvement backlog. Worth skimming before larger changes.
Data files (rendered at runtime)
src/data/programs.json— list of available programs with their display names, optional inriktningar (specializations), and a study-plan URL.src/data/<PROGRAM>.json— program-specific course datasets (currently CTFYS, CTMAT, CFATE, COPEN, CINEK, TIEMM). Each course includes fields such ascode,name,totalCredits,periodCredits(P1–P4, either flat per-year or by-year for multi-year courses),year,prerequisites, and theexams/reexamsarrays. Programs may also includeoptionGroupentries for course-choice slots.src/data/<PROGRAM>-cosmetics.json— per-course color-family assignments (capped at 5 families).src/data/kth-colors.json— KTH color palette used for fills/strokes in the visualization.src/data/academic-periods.json— academic period definitions (P1–P4) withstart,end,examStart,examEnd,reExamStart,reExamEndas ISO date strings. These are converted to Date objects insrc/types/course.ts.
How data is consumed
- The app loads program-specific JSON files based on user selection and maps
periodCreditsinto the internalcreditsarrays used by the visualization. academic-periods.jsonprovides the timeline boundaries and exam/re-exam ranges. The visualization readsacademicPeriodsexported fromsrc/types/course.ts.- Cosmetics files provide program-specific visual customizations (colors, positions) that override defaults.
Exam/re-exam markers
- By default each course has
examsandreexamsfields (arrays of period ids like"P2") set to the exam period following the course's last study period. - The visualization draws a filled circle (KTH brick color) for an exam and an open circle (stroke only) for a re-exam. Markers are positioned horizontally at the midpoint of the exam/re-exam period and vertically slightly above the course bar.
Visual Connectors: Courses spanning consecutive periods in the same year show visual connector fills between their bars, creating a unified appearance. Only the first bar in a sequence displays the course label.
Interactive Focus Mode: Click any course to highlight it and show detailed information in an expanding info box at the bottom. Focus mode dims other courses and shows only the selected course's prerequisite arrows and connectors.
Layer Visibility Toggle: The legend allows toggling visibility of different visual layers (course bars, borders, connectors, arrows, exam markers, etc.).
Export Functionality:
- SVG Export: Downloads the visualization as a vector SVG file with embedded Figtree font CSS so the file renders correctly outside the browser.
- PNG Export: Rasterises the on-screen SVG to a high-DPI PNG entirely in the browser.
- PDF Export: Server-side PDF generation using Puppeteer with Chrome for perfect font rendering and vector graphics. Configured for Vercel deployment with
@sparticuz/chromium.
Tooltip Information: Hover over courses to see total credits and per-period credit breakdown.
Bilingual UI (Swedish/English): Toggle from the export menu. The choice is reflected in the URL (?l=sv or ?l=en).
Shareable URL state: The selected program, language, hidden layers, and option-group picks are mirrored into the query string so a particular view can be linked or bookmarked.
Specializations (inriktningar) and option groups: Programs that split their bachelor years by inriktning (e.g. CINEK) get a SpecializationFilter UI that AND-filters courses across spec groups. Course-choice slots (e.g. thesis-track options) are modelled as optionGroup entries and rendered with a striped pattern; clicking opens a selection modal.
Troubleshooting
- Port 3000 already in use: find and kill the process
lsof -iTCP:3000 -sTCP:LISTEN -n -Pthenkill <PID>. - Suspended dev job (Ctrl+Z): resume with
fgor start a background server withnohupas shown above. - Type errors: run
npx tsc --noEmitto see TypeScript diagnostics. - PDF export not working on Vercel: Ensure
vercel.jsonis deployed with the project and@sparticuz/chromiumis in dependencies.
Key production dependencies:
next(16.x) — React frameworkreact(19.x) — UI libraryd3(7.9.x) — Visualization and data manipulationpuppeteer-core(23.x) — Headless browser control for PDF generation@sparticuz/chromium(141.x) — Serverless-compatible Chrome binary for Vercel
Development dependencies include TypeScript, ESLint, and Tailwind CSS.
vercel.json— Vercel deployment configuration with increased memory (1800MB for Hobby plan, can be increased to 3008MB on Pro) and timeout (60s) for the PDF export API route.tsconfig.json— TypeScript configuration.next.config.ts— Next.js configuration.package.json— Project manifest and scripts (dev, build, start).
The source code in this repository is released under the MIT License — see LICENSE.md. You're welcome to use, fork, and adapt it; please keep the copyright notice intact.
The course data in src/data/ was compiled from KTH's publicly available study plans (utbildningsplaner) and is included here for visualisation purposes only. The underlying programme content belongs to KTH and is not covered by this repository's license. If you reuse the app for another institution or programme, please replace the data files with your own source.