112 exercises · 10 modules · Real in-browser SQL execution · Cyberpunk CLI aesthetic
PunkSQL is a mobile-first SQL learning platform with a cyberpunk terminal aesthetic. It's like Duolingo meets LeetCode — you write real SQL queries that execute in your browser using SQLite compiled to WebAssembly. No signup required, no cost.
Built for career switchers learning SQL from scratch, and for data professionals who want to stay sharp.
Screenshots coming soon
| Home | Editor | Learn | Profile |
|---|---|---|---|
| Boot sequence, daily challenge, quests, stats | Mobile SQL editor with swipe cursor & focus mode | 10-module skill tree with progressive unlocking | Level, XP, achievements with expand-to-see details |
Every query runs on a real SQLite database loaded via sql.js (WASM). Seven tables with e-commerce and HR data. Your SQL is validated against expected output — column order, row count, values.
DML and DDL challenges use SQLite SAVEPOINTs for isolation: each run executes inside a savepoint, captures the post-operation state via a verify query, then rolls back — so the database is never permanently modified during validation.
The code editor was designed specifically for writing SQL on a phone:
- Swipe cursor — swipe anywhere on the editor to move the cursor (no tiny tap targets)
- Keyword buttons — collapsible panels for TBL names, COL names, and SQL keywords
- Focus mode — hides all chrome to maximize editor space (tap ◉ to toggle)
- Auto-scroll — editor follows the cursor as you navigate
- RESET button — one tap to clear and start over
- Desktop shortcuts —
Cmd+Enterto run,Escapeto go back
- 20 levels with progressive XP thresholds (25 → 11,200 XP)
- 12 achievements with real unlock conditions (first query, module completion, DML/DDL mastery, solve counts, level milestones)
- Level-up animation — full-screen number with bounce + glow
- Badge unlock animation — spinning icon with achievement name
- Sound effects — ascending arpeggio on correct, buzz on wrong, fanfare on level-up (Tone.js)
- XP only on first solve — no farming, progress is real
- Daily challenge — rotates based on day of year, +100 XP bonus
Full EN/PT-BR support. Every challenge description, quiz question, flashcard, UI element, and achievement has both languages. Toggle with the EN/PT switcher in the top bar.
XP, solved challenges, and language preference are saved to browser storage and survive page refreshes. When signed in, progress is synced to the cloud via Supabase and accessible across devices.
| Type | Count | Description |
|---|---|---|
| SQL Challenges | 112 | EASY → EXPERT, real SQL execution, DML/DDL with savepoint isolation |
| Quiz Questions | 60 | 6 per module, multiple-choice, 15s timer |
| Flashcards | 50 | Swipeable cards with 3-life system, per-difficulty stats |
| Achievements | 12 | First Query → SQL Master (solve all 112), Data Surgeon, Schema Architect |
| Levels | 20 | Progressive XP curve |
| Modules | 10 | SELECT → DDL, sequential unlocking |
Module 1: first_query SELECT, FROM, DISTINCT, LIMIT, COUNT
Module 2: filtering WHERE, AND/OR, IN, LIKE, BETWEEN
Module 3: sorting ORDER BY, ASC/DESC, multi-column, LIMIT
Module 4: aggregations COUNT, SUM, AVG, MIN/MAX, GROUP BY, HAVING
Module 5: joins INNER JOIN, LEFT JOIN, multi-table, ON
Module 6: subqueries Scalar, NOT IN, EXISTS, correlated
Module 7: window_fn ROW_NUMBER, RANK, DENSE_RANK, LAG/LEAD, PARTITION BY
Module 8: ctes WITH, chained CTEs, CASE WHEN, recursive patterns
Module 9: dml INSERT, UPDATE, DELETE, NULL handling, duplicates,
data cleaning (raw_sales + employee_salaries tables)
Module 10: ddl CREATE TABLE/VIEW/INDEX, ALTER TABLE, DROP,
SAVEPOINTs for atomic schema changes
All queries run against these seven tables, seeded once when the page loads.
| Table | Columns | Rows |
|---|---|---|
customers |
id, name, email, city, country, signup_date |
10 |
products |
id, name, category, price, stock |
10 |
orders |
id, customer_id, order_date, total_amount, status |
16 |
order_items |
id, order_id, product_id, quantity, unit_price |
24 |
reviews |
id, product_id, customer_id, rating, review_date |
12 |
Two tables with intentionally dirty data for hands-on cleaning practice.
| Table | Columns | Intentional flaws |
|---|---|---|
raw_sales |
id, product_id, quantity, unit_price, discount, sale_date, customer_name |
Negative quantities, NULL prices, impossible discounts (>1.0), zero quantities, duplicate records, NULL customer names |
employee_salaries |
id, name, department, salary, hire_date |
Negative salaries, NULL names, NULL departments, zero-salary entries |
Challenges cover: exploring dirty data with SELECT, finding duplicates with GROUP BY + HAVING, fixing negatives with UPDATE, filling NULLs with AVG, archiving with INSERT INTO...SELECT, atomic multi-step cleanup with SAVEPOINT, and creating clean snapshots with CREATE TABLE AS SELECT.
| Layer | Technology | Purpose |
|---|---|---|
| Framework | Next.js 16 | SSR, routing, deployment |
| UI | React 19 | Component framework |
| State | Zustand | Client-side game state & query history |
| SQL Engine | sql.js | SQLite compiled to WASM, runs in-browser |
| Auth & Sync | Supabase | Optional sign-in + cloud progress sync |
| Sound | Tone.js | Synthesized sound effects |
| Font | Share Tech Mono | Monospace terminal aesthetic |
| Storage | Browser localStorage |
Progress persistence without an account |
| Deploy | Vercel | Edge CDN, auto-deploy from GitHub |
The SQL engine runs entirely in-browser. No backend is required to play. Auth and cloud sync are optional features powered by Supabase.
# Clone
git clone https://github.com/eduardocornelsen/punksql.git
cd punksql
# Install
npm install
# Run
npm run dev
# Open http://localhost:3000Requires Node.js 18+.
Other available commands:
npm run build # Production build
npm start # Start production server (after build)
npm run lint # Run ESLintThe app works without any env vars — all SQL execution runs in-browser. To enable sign-in and cloud progress sync, create a .env.local file:
NEXT_PUBLIC_SUPABASE_URL=your-project-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
These values come from your Supabase project settings. Without them, the app falls back to localStorage-only persistence.
Or manually:
npx vercelSet NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY in your Vercel project's environment variables if you want auth and cloud sync.
PunkSQL uses Next.js API routes for auth and progress sync, so it requires a Node.js runtime — it is not a purely static site.
npm run build
npm start # Starts the Node.js server on port 3000punksql/
├── src/
│ ├── app/
│ │ ├── page.js # Main page (dynamic import, no SSR)
│ │ ├── layout.js # Root layout, SEO metadata, PWA head
│ │ └── api/
│ │ ├── auth/callback/ # Supabase OAuth callback
│ │ ├── progress/ # Cloud progress sync (GET / PUT)
│ │ └── analytics/ # Attempt logging
│ ├── components/
│ │ ├── PunkSQL.jsx # Complete app (~3300 lines, single file)
│ │ └── AuthProvider.jsx # Supabase auth context
│ ├── hooks/
│ │ └── useProgress.js # localStorage ↔ Supabase sync logic
│ ├── stores/
│ │ └── useGameStore.js # Zustand store (game state, query history)
│ └── lib/
│ └── supabase/ # Supabase client (browser + server)
├── public/
│ ├── manifest.json # PWA manifest (installable as mobile app)
│ └── favicon.svg # Cyberpunk icon
├── package.json
├── next.config.js
└── README.md
The core app lives in PunkSQL.jsx. This is intentional — it's a self-contained artifact that can be ported to React Native or run as a standalone page with minimal changes.
Cyberpunk CLI — inspired by terminal aesthetics with neon accents.
| Token | Hex | Usage |
|---|---|---|
void |
#020410 |
Background |
cyan |
#00F0FF |
Primary accent, links, active states |
green |
#00FF88 |
Success, solved, correct |
amber |
#FFB800 |
Warnings, streaks, XP |
red |
#FF3050 |
Errors, lives, wrong answers |
purple |
#D0A0FF |
Table names |
Effects: CRT scanlines, vignette overlay, pulse glow, cursor blink, flip card animation, fade-slide transitions.
PunkSQL sits in a crowded space alongside DataLemur, HackerRank, LeetCode, and StrataScratch. Here's how it compares and where it's headed.
| Feature | PunkSQL | DataLemur | HackerRank | LeetCode | StrataScratch |
|---|---|---|---|---|---|
| Mobile-first | ✅ | ❌ | ❌ | ❌ | ❌ |
| No signup required | ✅ | ❌ | ❌ | ❌ | ❌ |
| Runs fully offline (WASM) | ✅ | ❌ | ❌ | ❌ | ❌ |
| Gamification (XP / levels / achievements) | ✅ | partial | partial | partial | ❌ |
| DML + DDL challenges | ✅ | ❌ | ❌ | ❌ | ❌ |
| Flashcards + spaced repetition | ✅ | ❌ | ❌ | ❌ | ❌ |
| PWA installable | ✅ | ❌ | ❌ | ❌ | ❌ |
The mobile-first + offline + no-signup combination is a genuine moat — no other SQL learning platform does all three.
Solution explanations & hints — DataLemur shows a full editorial after each submission: why this query works, common mistakes, alternative approaches. PunkSQL validates correctness but leaves the learner without guidance. LeetCode and DataLemur both offer 3-tier progressive hints that reduce frustration without giving away the answer.
Real interview question tagging — DataLemur and StrataScratch brand challenges as "asked at Meta / Airbnb / Stripe". This creates perceived value and urgency. PunkSQL challenges are well-crafted but unframed.
Account-based persistence + shareable profile — Progress currently lives in localStorage. Users can't show their SQL rank to an employer, and progress is lost on a browser clear. HackerRank certificates are a primary reason people use the platform.
Per-skill analytics — StrataScratch shows a radar chart of SQL competency (JOINs, window functions, aggregations) so users know exactly what to study. PunkSQL tracks XP but not skill accuracy per topic.
Community solutions — LeetCode's comment sections are where users learn the most — alternative approaches, gotchas, debate. PunkSQL has no community layer.
SQL dialect coverage — HackerRank and StrataScratch support PostgreSQL, MySQL, and MS SQL. PunkSQL is SQLite-only; window function and date syntax differs in production databases.
- 112 SQL challenges with real execution (DML + DDL included)
- Gamification (levels, achievements, XP)
- Mobile-first editor with swipe cursor
- EN/PT-BR bilingual
- Sound effects
- Persistent storage (localStorage)
- PWA-ready
- DML data cleaning module (DELETE, UPDATE, INSERT INTO SELECT, SAVEPOINT)
- DDL schema module (CREATE TABLE/VIEW/INDEX, ALTER TABLE, DROP, SAVEPOINT)
- Supabase auth wiring + cloud progress sync
- Solution explanations — annotated query + plain-English breakdown shown after solve
- 3-level hints — clause hint → skeleton query → fill-in-the-blank, each with a small XP cost
- Company archetype tags — challenges labelled as e-commerce / fintech / analytics / social media interview-style
- Editor refactor — linter upgrades, keyboard remapping (*,
,,(,)keys), smart indentation, hamburger fix (#5)
| Priority | Feature | Effort | Impact | Why it matters |
|---|---|---|---|---|
| 1 | Shareable profile card — generate a linkable SQL rank card from the existing auth | Low | High | Lets users prove skills; shareable cards drive organic growth |
| 2 | Skills radar — per-module accuracy chart showing strong vs. weak SQL topic areas | Low | High | Gives users a study direction; closes StrataScratch gap |
| 3 | Firebase auth migration — replace Supabase with Firebase; fixes localhost OAuth redirect loop and file:// origin conflict for Android (#4) |
Medium | High | Unblocks native Android build; removes SSR auth complexity |
| 4 | Game mode separation — split into SYSTEM_STORY (linear campaign with narrative hooks) and BOUNTY_BOARD (daily rotating challenge with streak tracker) (#2) |
Medium | High | Gives learners two distinct goals; bounty board drives daily retention |
| 5 | Native Android app via Capacitor — wrap Next.js static export as a signed .aab for Google Play Store submission (#3) |
High | High | Reaches users who won't open a browser app; depends on Firebase migration |
| 6 | SQL Odyssey rebrand + Story Mode — full rebrand to minimalist terminal aesthetic; 3-campaign text-driven story mode (AI awakening, corporate forensics, deep-space recovery) (#6) | Very High | Very High | Major product evolution; replaces cyberpunk theme with a scalable narrative engine |
| 7 | PGlite migration — swap sql.js for PGlite (PostgreSQL compiled to WASM) | High | Medium | Real-world dialect accuracy; PostgreSQL is the dominant production DB |
| 8 | Community solutions feed — 2–3 curated alternative solutions per challenge | High | High | Addresses the #1 reason people stay on LeetCode |
| 9 | Premium tier — gate skills radar and shareable certificate | High | High | Sustainability; mirrors DataLemur/StrataScratch monetization model |
- Streak system with daily reset
- Challenge validation flexibility (column-order agnostic)
- Interview prep mode (timed, no hints)
- Python track (Pyodide WASM)
Contributions are welcome. To get started:
- Fork the repo and create a branch:
git checkout -b feat/your-feature - Make your changes and run
npm run lintto check for issues - Open a pull request with a clear description of what you changed and why
Areas that need help:
- More SQL challenges — especially EXPERT-level real-world scenarios
- Quiz questions — cover edge cases and SQL gotchas
- Flashcard content — advanced topics like indexing, query optimization
- Accessibility — screen reader support, keyboard navigation
- Testing — automated tests for SQL validation logic
- Translations — add more languages beyond EN/PT-BR
- SQL data model inspired by the Olist Brazilian E-Commerce dataset
- Gamification patterns from Duolingo
- Challenge format from LeetCode and DataLemur
- SQL engine: sql.js by the sql.js contributors
MIT — use it however you want.
Built with >_ and ◈ by Eduardo Cornelsen
Learn SQL. Level up. Ship queries.