A classroom template for a browser game built with Phaser and Supabase.
Install Node, add your Supabase keys, and run the game. No Docker, no Java, and no separate backend server.
This GitHub repo is the reusable template. Each year the instructor copies it into a class repository. Students clone that class repo (not this template) and use it for the year.
Yearly placeholders look like «THIS». Instructors replace every «…» marker when creating the class copy (see For Instructors).
- Features
- Prerequisites
- Initial Setup
- Supabase: Local Dev vs Production
- Supabase Setup (Local Dev)
- Environment Variables
- Quick Start
- Verify Everything Works
- Development URLs
- Useful Commands
- Daily Workflow
- Project Structure
- Talking to Supabase from Code
- What Comes Next
- Database and Migrations
- Deploying to GitHub Pages
- For Instructors
- Troubleshooting
- Phaser 4 + TypeScript + Vite (hot reload)
- Small demo that checks your Supabase connection
- Personal local-dev Supabase project per student; one shared production project for the class site
- Supabase helpers live in
src/services/(not inside Phaser scenes) - SQL migrations stored in git under
supabase/migrations/ - GitHub Actions CI and GitHub Pages deploy
- Node.js 24.21.0 (see
.nvmrc) - npm (comes with Node)
- Git
- Visual Studio Code (recommended)
- A free Supabase account
- A GitHub account
Windows: use Git Bash for the commands in this README.
School network
On a restricted school network you may need:
- An alternate NVM download mirror
- A temporary SSL workaround for
npm install
Details are in Initial Setup.
Mac first: if you are on a Mac, start Xcode Command Line Tools before anything else. The install often takes 15-20 minutes. Kick it off, then continue with VS Code / accounts while it runs.
Visual Studio Code
- Download: https://code.visualstudio.com/download
Git
- Windows: https://gitforwindows.org/ (includes Git Bash)
- Mac:
xcode-select --install(do this at the start of class on setup day)
Mac: Xcode Command Line Tools
This provides Git (and other build tools) on macOS. The download/install commonly takes 15-20 minutes.
xcode-select --installClick Install if prompted. Leave the installer running and move on to VS Code, GitHub, and Supabase account setup while you wait.
Where to put it: keep class projects in ~/Development under your home folder (on a Mac: /Users/yourname/Development). On a Mac, Documents is synced with iCloud (Desktop often is too). Do not clone into an iCloud-synced folder. iCloud sync flips file permissions and makes Git show every file as changed.
mkdir -p ~/Development
cd ~/DevelopmentUse the class repository your instructor shared (a yearly copy of this template).
- Open the class repo on GitHub
- Click Code and copy the URL
- In VS Code: Clone Repository, paste the URL, and choose
~/Developmentas the parent folder
Or in Git Bash / Terminal (replace with your class repo):
cd ~/Development
git clone https://github.com/«CLASS_GITHUB_ORG»/«CLASS_REPO_NAME».git
cd «CLASS_REPO_NAME»Right after cloning, ignore file-permission noise if your machine still reports mode flips:
git config core.filemode falseSame idea for OneDrive or Google Drive: keep the repo on a normal local path like ~/Development, not inside a cloud-synced folder.
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bashAdd this to your shell config:
- Windows (Git Bash):
nano ~/.bash_profile - Mac:
nano ~/.zshrc
export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvmSave (Ctrl+X, then Y, then Enter), then open a new terminal.
School network
NVM_NODEJS_ORG_MIRROR=http://nodejs.org/dist nvm install
nvm use
node -v # should show v24.21.0Home network
nvm install
nvm use
node -v # should show v24.21.0School network
npm run install:schoolHome network
npm installFollow Supabase: Local Dev vs Production, then Supabase Setup (Local Dev) and Environment Variables.
node -v # v24.21.0
git --version
npm -v| Project | Who creates it | Used by | Where the keys go |
|---|---|---|---|
| Local / development | Each student (own project) | npm run dev |
Local .env (not committed) |
| Production | The class (one shared project) | GitHub Pages | GitHub Actions variables on the class repo |
Your laptop Class GitHub Pages site
─────────── ──────────────────────
.env → your Supabase project Actions vars → class production project
npm run dev push to main → deployed site
Why two projects?
- You can reset or break your own database without affecting the public site
- Daily work does not all hit one shared database
- The live site stays on a stable production project
When the schema changes, run the same SQL from supabase/migrations/ on both your local-dev project and the class production project.
Do not put your personal local-dev keys in GitHub Actions. Do not commit production keys into the repo.
Every student creates their own Supabase project for local development.
- Go to https://supabase.com/ and sign in
- Open (or create) your organization, then click New project
- Fill in the form:
| Field | What to choose |
|---|---|
| Organization | Your personal org, unless your instructor says otherwise |
| GitHub (optional) | Leave unset; migrations live in this game repo |
| Project name | e.g. alex-projects2-dev (your name + dev) |
| Database password | Click Generate a password and save it somewhere safe. You rarely need it here, but you cannot view it again later. |
| Region | Closest to you (Americas is fine for most US West classrooms) |
- Under Security, use:
| Setting | Choose | Why |
|---|---|---|
| Enable Data API | On | Needed for supabase-js |
| Automatically expose new tables | Off | Access stays intentional; our SQL includes the required GRANTs |
| Enable automatic RLS | On | Good default for this course |
- Click Create new project and wait until it finishes (often 1-2 minutes)
- Open Project Settings → API
- Copy:
- Project URL (
https://xxxxx.supabase.co) - Publishable key (
sb_publishable_…)
- Project URL (
Never put the secret key (sb_secret_…) in this app or in .env.
- Open SQL Editor → New query
- Paste all of
supabase/migrations/001_initial.sql - Click Run
That creates demo_messages and one hello row.
Continue with Environment Variables.
Production setup (one class project for GitHub Pages) is covered under Deploying to GitHub Pages.
.env is for your local-dev Supabase project only.
cp .env.example .envEdit .env:
VITE_SUPABASE_URL=https://YOUR_PROJECT_REF.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=YOUR_SUPABASE_PUBLISHABLE_KEYRestart npm run dev after changing .env.
| Value | Safe in the browser? | Notes |
|---|---|---|
VITE_SUPABASE_URL |
Yes | Project URL |
VITE_SUPABASE_PUBLISHABLE_KEY |
Yes | Publishable key (sb_publishable_…) |
Secret key (sb_secret_…) |
No | Never commit or add to Vite / Pages |
.env is gitignored. Production keys go in GitHub Actions variables on the class repo.
Anything shipped to GitHub Pages is public. Security comes from Auth and Row Level Security later, not from hiding the publishable key.
Finish setup above first, then:
npm run devOpen the URL Vite prints (usually http://localhost:5173). You should see Connected: demo_messages loaded and a hello message.
npm run devstarts without errors- The Phaser canvas appears
- Status shows Connected: demo_messages loaded
- You see something like
#1 Hello from Supabase! - Refresh still works
Optional:
npm run smokeIf that passes, your computer, .env, and local-dev Supabase project are set up correctly.
| Service | URL | Description |
|---|---|---|
| Game (local) | http://localhost:5173 | Phaser app + connection demo |
| Your local Supabase | https://supabase.com/dashboard | Project from your .env |
| Class production Supabase | https://supabase.com/dashboard | Shared project for GitHub Pages |
| GitHub Pages | https://«CLASS_GITHUB_ORG».github.io/«CLASS_REPO_NAME»/ |
Public site (after deploy is set up) |
npm run dev Start the local game server
npm run build Typecheck and build for production
npm run preview Preview the production build locally
npm run lint Run ESLint
npm run format Format with Prettier
npm run format:check Check formatting (CI)
npm run smoke Check Supabase connectivity from the terminal
npm run install:school npm install with a temporary SSL workaround
git pull
npm install # only if dependencies changed
npm run dev- Edit scenes in
src/game/scenes/ - Put Supabase calls in
src/services/ - Vite reloads the browser when you save
- If you change the database schema, add SQL under
supabase/migrations/, run it on your local-dev project, and make sure the class production project gets the same SQL before or when you deploy
Before you push:
npm run lint
npm run format:check
npm run buildStop the dev server with Ctrl+C.
├── .github/workflows/ # CI + Pages deploy
│ ├── ci.yml
│ └── deploy.yml
├── public/assets/ # Images, audio, etc.
├── src/
│ ├── game/
│ │ ├── scenes/ # BootScene, DemoScene (replace with your game)
│ │ └── config.ts
│ ├── services/
│ │ ├── supabase.ts # Supabase client
│ │ └── demo.ts # Demo helpers
│ ├── main.ts
│ └── style.css
├── supabase/migrations/
│ └── 001_initial.sql
├── scripts/smoke-test.ts
├── .env.example
├── .nvmrc
├── AGENTS.md # Notes for AI coding tools
├── package.json
├── vite.config.ts
└── README.md
The starter migration creates demo_messages only to prove connectivity.
import { getDemoMessages } from '@/services/demo';
const rows = await getDemoMessages();Call helpers from src/services/ instead of writing Supabase queries inside scenes.
When you add Auth, scores, or saves, add new files next to demo.ts (for example auth.ts, gameData.ts).
This template only checks the connection. The class still needs to build:
- Authentication (register / sign in / sign out)
- Profiles and ownership
- Game tables (scores, inventory, saves, …)
- Row Level Security for private data
- The actual Phaser game
Keep schema in git, not only in the Supabase dashboard.
- Starter file:
supabase/migrations/001_initial.sql - Apply by pasting into the Supabase SQL Editor
- Run new migrations on each student’s local-dev project and on the class production project
- Replace the demo table when your real schema is ready
demo_messages is publicly readable so setup works before Auth. Private player data should use Auth and stricter RLS later.
The public site must use the class production Supabase project, not a student’s local-dev project.
(Instructor / class once)
- Create one shared project (same form as Create the project)
- Name it something like
«CLASS_PRODUCTION_SUPABASE_NAME»(e.g.projects2-26-27-production) - Same security settings: Data API on, automatically expose new tables off, automatic RLS on
- Run every file in
supabase/migrations/in that project’s SQL Editor - Copy the Project URL and publishable key
- Open the class repo Settings → Pages
- Set Source to GitHub Actions
- Open Settings → Secrets and variables → Actions → Variables and add:
VITE_SUPABASE_URL: production Project URLVITE_SUPABASE_PUBLISHABLE_KEY: production publishable key
- Allow GitHub Actions to run
push to main
→ Actions builds with VITE_BASE_PATH=/«CLASS_REPO_NAME»/
→ Build embeds production Supabase URL + publishable key
→ Publishes to GitHub Pages
Site URL:
https://«CLASS_GITHUB_ORG».github.io/«CLASS_REPO_NAME»/
The deploy workflow sets VITE_BASE_PATH from the repository name, so you usually do not edit it by hand.
VITE_BASE_PATH=/«CLASS_REPO_NAME»/ npm run build
npm run previewTo temporarily run against production data (do not commit these):
VITE_SUPABASE_URL=... VITE_SUPABASE_PUBLISHABLE_KEY=... npm run devSearch the class copy for « and replace every marker:
| Placeholder | Meaning | Example |
|---|---|---|
«CLASS_SITE_TITLE» |
Visible project title (README + index.html) |
Projects II 2026-27 |
«CLASS_GITHUB_ORG» |
GitHub org or user that owns the class repo | HolyNamesAcademy |
«CLASS_REPO_NAME» |
Class GitHub repository name | Projects-II-26-27 |
«CLASS_PRODUCTION_SUPABASE_NAME» |
Shared production Supabase project name | projects2-26-27-production |
Also set GitHub Actions variables on the class repo (not placeholders in files):
VITE_SUPABASE_URL: class production Project URLVITE_SUPABASE_PUBLISHABLE_KEY: class production publishable key
VITE_BASE_PATH is set automatically from the GitHub repository name in the deploy workflow (it should match «CLASS_REPO_NAME»).
Tip: GitHub Pages URLs use the org’s lowercase form (e.g. holynamesacademy.github.io), even if the org display name has capitals.
- Create a new class GitHub repo named
«CLASS_REPO_NAME»under«CLASS_GITHUB_ORG»(from this template, or copy its contents) - Replace every
«…»placeholder in the class copy (start with«CLASS_SITE_TITLE»in this README and inindex.html) - Create the class production Supabase project
«CLASS_PRODUCTION_SUPABASE_NAME»and applysupabase/migrations/ - Set Actions variables
VITE_SUPABASE_URLandVITE_SUPABASE_PUBLISHABLE_KEYon the class repo - Enable Pages → GitHub Actions
Setup-day tip (Macs): on the class period when students first set up their machines, have everyone run xcode-select --install in the first few minutes. Command Line Tools often take 15-20 minutes; starting late blocks cloning and Node install for the rest of the period.
CI and deploy workflows are skipped on this upstream template (HolyNamesAcademy/PhaserSupabaseTemplate). They run automatically on class copies under a different repo name. No workflow edits required.
Students then clone https://github.com/«CLASS_GITHUB_ORG»/«CLASS_REPO_NAME».git, each create a personal local-dev Supabase project + .env, and push to the class repo. Pages uses the class production project.
Start the next year with a fresh class repo (and usually a fresh production Supabase project) copied from this template again.
Do not fork the template into the class repo. Forks complicate student PRs (wrong base repo / confusing upstream). Keep two sibling repos in the same org and push/pull between them with an extra git remote.
| Role | Repo |
|---|---|
| Upstream template (shared fixes) | https://github.com/HolyNamesAcademy/PhaserSupabaseTemplate |
| Class copy (this year) | https://github.com/«CLASS_GITHUB_ORG»/«CLASS_REPO_NAME» |
What goes where
- Shared changes (setup docs, tooling, demo scene, CI, migrations students should inherit): edit this template first, then merge
template/maininto the class repo on a branch and open a PR. - Class-only changes (that year’s title/URLs, team access notes, production Supabase name, rulesets): edit the class repo only. Do not copy those back into this template.
On the class clone, add the template remote once:
git remote add template git@github.com:HolyNamesAcademy/PhaserSupabaseTemplate.gitBring template fixes into the class repo (prefer a branch + PR if main is protected):
git fetch template
git checkout -b sync/template-$(date +%Y%m%d)
git merge template/main
# resolve conflicts if any (usually README / AGENTS class-specific bits), then:
git push -u origin HEAD
# open a PR into mainOptional on the template clone (compare or cherry-pick the other way):
git remote add class git@github.com:«CLASS_GITHUB_ORG»/«CLASS_REPO_NAME».gitGit says every file changed (permissions / mode only)
Do not commit that. It is almost never a real project change. Git is seeing file modes flip (for example 644 to 755) after VS Code or a cloud-sync folder touches the tree.
In the repo folder:
git config core.filemode false
git restore .
git statusYou should be back to a clean tree (or only your real edits). Then make the name / code change and commit that alone.
If git status is still noisy, re-clone under ~/Development (not Mac Documents or Desktop, which are iCloud-synced), run git config core.filemode false again, and continue.
Local works but GitHub Pages shows wrong or empty data
- Local
.envis your dev project; Pages uses production from Actions variables - Confirm production has the same migrations
- Confirm Actions variables are the class production keys, not a student’s local-dev keys
Supabase is not configured
- Confirm
.envexists (from.env.example) - Confirm values are not still
YOUR_…placeholders - Restart
npm run devafter editing.env - Names must start with
VITE_
Permission denied for table
Common when Automatically expose new tables is off and grants were not applied.
grant usage on schema public to anon, authenticated;
grant select on table public.demo_messages to anon, authenticated;Or re-run the full supabase/migrations/001_initial.sql.
Also confirm:
.envusesVITE_SUPABASE_PUBLISHABLE_KEY- you restarted
npm run devafter editing.env - URL and publishable key are from the same project
Table missing / request failed
- Run
supabase/migrations/001_initial.sqlin the SQL Editor - Confirm
demo_messagesexists in Table Editor - Confirm URL and publishable key match that project
- Try
npm run smoke
Wrong Node version
nvm install
nvm use
node -v # should match .nvmrc (v24.21.0)npm install fails on school network
npm run install:schoolBlank page or missing assets on GitHub Pages
- Pages source is GitHub Actions
- Deploy workflow succeeded under Actions
- Actions variables are set
- Open the URL including the repo subpath (
/«CLASS_REPO_NAME»/)
Port 5173 already in use
Stop the other process, or use the alternate URL Vite prints.
Windows terminal issues
- Use Git Bash, not PowerShell
- In VS Code, use the terminal dropdown next to
+and choose Git Bash - Confirm you are in the project root (
lsshowspackage.json)
- Re-check setup, local Supabase, and
.env - Run
npm run smokeand read the error - Check the browser console
- Ask your instructor or a classmate. Include what you tried and the exact error text