Learn Three.js, WebGL and GLSL — from the dot product to raymarching — by writing every piece yourself.
Open the site · Roadmap · Playground · Releases
Shaderpath is a bilingual (English / Vietnamese) course for real-time graphics on the web. It runs from vectors and matrices, through raw WebGL, GLSL, Three.js and React Three Fiber, up to raymarching, GPGPU simulation, post-processing, PBR and production performance. Every lesson teaches one mental model and lets you change it live on the page. You can read everything without an account. Sign in only to save progress, notes and your review schedule.
Lessons — one mental model each, with a mind map, objectives and common mistakes |
Live demos — adjust the parameter the lesson is about and watch it change |
GLSL playground — Monaco editor, live WebGL2 preview, 35 presets |
Roadmap — 14 tracks as a map, with your progress on it |
- 162 lessons in 14 tracks, about 136 hours. Each lesson covers one idea in 20–45 minutes. Modules end in mini-build checkpoints, and the course ends with four capstone projects.
- 124 live demos. They are built with raw WebGL2, Three.js or React Three Fiber, and their controls change exactly the thing the lesson explains.
- Exercises with worked answers. Concept questions, TypeScript tasks and shader tasks, each with hints, a checklist and a written solution.
- Spaced-repetition review. Recall cards for 123 theory lessons, scheduled with an SM-2-style algorithm from the grades you give yourself.
- GLSL playground. A Monaco editor with GLSL highlighting, a live WebGL2 preview, exact error lines, 35 presets and saved snippets.
- Notes, bookmarks and search. Notes are anchored to headings, and Ctrl+K searches every lesson in the language you're reading.
- Stats. Streaks, a 26-week activity heatmap and time spent per track.
- Adaptive quality. Demos detect a quality tier and cap the canvas DPR and effects to match. A manual choice in Settings always wins.
- Your data stays yours. Progress exports to versioned JSON and imports back with a preview. Replace and merge both run in a single transaction.
| # | Track | Lessons | Time |
|---|---|---|---|
| 00 | Math Foundations for Graphics | 14 | 9.3 h |
| 01 | Raw WebGL | 15 | 9.0 h |
| 02 | GLSL Fundamentals | 10 | 6.1 h |
| 03 | Three.js Core | 14 | 9.3 h |
| 04 | React Three Fiber | 13 | 7.9 h |
| 05 | GSAP & Advanced Animation | 13 | 8.3 h |
| 06 | Custom Shaders in Three.js | 10 | 6.6 h |
| 07 | Procedural & Noise | 10 | 6.8 h |
| 08 | Raymarching & SDFs | 13 | 9.1 h |
| 09 | GPGPU, Particles & Simulation | 12 | 8.8 h |
| 10 | Post-processing | 10 | 6.8 h |
| 11 | PBR & Lighting Theory | 11 | 7.5 h |
| 12 | Performance & Production | 13 | 8.8 h |
| 13 | Capstone Projects | 4 | 32 h |
| Area | Tools |
|---|---|
| App | Next.js 16 (App Router, Turbopack), React 19, TypeScript, next-intl |
| UI | Tailwind CSS v4, shadcn/ui (Base UI), Google Sans |
| Graphics | three.js r185, React Three Fiber 9, drei, @react-three/postprocessing, GSAP 3, raw WebGL2 |
| Content | MDX, KaTeX, Shiki, a typed content registry generated at build time |
| Editor | Monaco with a custom GLSL tokenizer |
| Data | Postgres on Supabase (Auth + row-level security), Drizzle ORM |
| State | Zustand, TanStack Query |
| Tests | Vitest, Playwright, axe |
Requirements:
- Node.js 20+ and pnpm (the version is pinned in
packageManager, socorepack enablepicks it up) - A Postgres database (Supabase in production)
- Docker, only to run the test suites, which start a throwaway Postgres
pnpm install
cp .env.example .env.local # fill in DATABASE_URL, DIRECT_URL and the Supabase keys
pnpm db:migrate # creates the schema
pnpm dev # http://localhost:3000Migrations do not run on boot. The app is deployed to serverless functions, where a boot hook fires on every cold start, and DDL does not belong on the request path.
Production build:
pnpm build # regenerates the lesson registry and search index, then next build
pnpm startDATABASE_URLis the runtime connection. On Supabase, use the transaction pooler (port 6543). Each serverless function opens its own connection, so the direct connection runs out under light traffic.db/client.tsdetects:6543and switches to a single connection with prepared statements off, which that pooler requires.DIRECT_URL(port 5432) is used only bypnpm db:migrate. Running DDL through the transaction pooler is not safe.- Migrations live in
db/migrations/. After changingdb/schema.ts, generate them withpnpm db:generateand apply them withpnpm db:migrate. - The database holds progress, notes, bookmarks, review scheduling and settings, never lesson content. Every table is protected by row-level security.
pnpm verify:rlschecks the policies against the live database.
- Create a Supabase project and copy both connection strings from Project Settings → Database → Connection string.
- In Vercel Settings → Environment Variables, set
DATABASE_URLto the transaction-pooler URL (port 6543), plusNEXT_PUBLIC_SUPABASE_URLandNEXT_PUBLIC_SUPABASE_ANON_KEY. SetDIRECT_URLthere only if you migrate from CI. - Apply the schema once from your machine:
DIRECT_URL=... pnpm db:migrate - In Supabase Authentication → URL Configuration, set the Site URL and redirect URLs to your domain.
- Deploy. Re-run step 3 whenever a migration is added, because the app will not create tables for you.
- In the app (recommended): go to Settings → Data. Export downloads
shaderpath-progress-<date>.json, tagged with a schema version. Import validates the file, previews the row counts and downloads a backup of your current state first. It then replaces or merges your data in one transaction. - On the database side: use Supabase's own backups, or run
pg_dumpagainstDIRECT_URL.
Imports with a mismatched schemaVersion are rejected outright. Re-export from the same app version instead of editing the JSON by hand.
content/tracks/*.tsholds the track, module and lesson metadata. Slugs are frozen, because renaming one breaks notes and bookmarks.- Each lesson lives in
content/lessons/<track>/<slug>/:theory.en.mdxandtheory.vi.mdxreferences.ts,exercises.tsandreview-cards.tsdemo.tsx, with any sibling.vertand.fragfiles
- Run
pnpm gen:registryto regenerate the typed registry after adding files. pnpm lint:contentis the quality gate. It checks heading parity between the two languages, citations, exercise and review-card rules, and KaTeX glyph safety.- Figures are authored as English SVG.
pnpm gen:figuresrenders the Vietnamese copies fromcontent/figures-i18n/, andpnpm lint:figuresfails if a copy drifts from its source.
pnpm typecheck # tsc --noEmit
pnpm lint # eslint
pnpm test # vitest (starts a throwaway Postgres in Docker)
pnpm test:e2e # playwright (boots its own server on :3100, with a fresh database per run)
pnpm audit:guards # no custom CSS, strict TS, full content and figure lintThe source code is released under the MIT License. The educational content is © 2026 dris1153, all rights reserved. That covers the lesson text, exercises, review cards, mind maps and figures. See LICENSE for exactly which files are included.