Daniel's Town ▶ Play the game
Portrait of Daniel Shi

Daniel Shi

Simon Fraser University - BSc Computing Science (Software Systems), minors in Math & Economics

Problem spotter (I have a lot of problems), builder, and lover of good web experiences.

About

Hi, I'm Daniel, a Computing Science student at Simon Fraser University in the Software Systems stream, with minors in Math and Economics.

Right now I'm writing a relational storage engine in C++17. I also run the tech side of two student organisations, The Forum and MISA, where I ship event platforms that real people register and pay through. I compete with SFU's ICPC team, and I teach AP Computer Science (Java and Python) at StudyPug.

Away from the keyboard you'll find me behind a camera, bouldering, or training in grappling.

Languages

  • C
  • C++
  • TypeScript
  • JavaScript
  • Python
  • Java
  • SQL
  • HTML
  • CSS
  • GLSL

Frameworks

  • React
  • Next.js
  • Node.js
  • Express
  • Tailwind CSS
  • three.js
  • D3.js
  • Zod
  • OpenCV
  • scikit-learn

Data & Cloud

  • PostgreSQL
  • Redis
  • Supabase
  • Nginx
  • Caddy
  • AWS Lightsail
  • DigitalOcean
  • Vercel
  • GCP

APIs & Services

  • Stripe API
  • Gemini API
  • Meta CAPI
  • Brevo CRM

Tools

  • Git
  • GitHub Actions
  • Docker
  • Docker Compose
  • Vite
  • Vitest
  • GoogleTest
  • Figma
  • CMake
  • Linux
  • GA4
  • GDB
  • UML

Concepts

  • OOP
  • Multithreading
  • POSIX

Certifications

Projects

YASE - Yet Another Storage Engine

Sep 2026 - Present

A learning project. Building a relational database storage engine from scratch in C++17.

  • C++17
  • POSIX
  • Multithreading
  • GoogleTest
  • CMake
Read the write-up

Goal

I wanted to understand how a database actually stores data, below the SQL. So I'm building the storage layer myself: how pages move between disk and memory, what stays cached, and when writes reach disk.

Design choices

  • Direct I/O. A block-based storage layer opens files with O_DIRECT and uses 4 KB-aligned buffers to bypass the kernel page cache. Thread-safe pread/pwrite calls and lock-free atomic page allocation let many threads do I/O at once.
  • Buffer pool manager. An O(1) LRU replacer with pin counting and dirty-page write-back. Pinned frames are never evicted, and modified pages are flushed before their frame is reused.
  • Heap files. Bitmapped data pages plus a directory-based heap file in which each directory page tracks 1,024 data pages. Record IDs are stable, bit-packed 64-bit values.

What I learned

  • Why databases manage their own memory instead of trusting the OS page cache.
  • How to keep invariants like "pinned pages are never evicted" true under concurrency, and how to test them with GoogleTest.
Screenshot of Daniel's Town (this site)

Daniel's Town (this site)

Aug 2026 - Present

This portfolio, an 8-bit voxel town built with three.js and a hand-written palette pipeline.

  • TypeScript
  • three.js
  • GLSL
  • Vite
  • Express
  • Zod
Read the write-up

Problem

Portfolios tend to look alike. I wanted one that shows rendering and full-stack skill just by being used, and that still works as a plain, fast, accessible page for anyone who'd rather skip the game.

Design choices

  • A custom render pipeline. The scene renders at 320×180 into a nearest-filtered render target. A fullscreen shader then applies 4×4 Bayer dithering and snaps every pixel to the PICO-8 palette.
  • Every model is code. Buildings, props and the player are voxel data built with small helpers (box, roof, compose…), then meshed with face culling and baked shading. Repeated props use InstancedMesh.
  • Content as Markdown. Projects and roles are Markdown files validated with Zod at build time. The same parser pre-renders a semantic HTML version for crawlers and the plain view.

Impact

  • Adding a project or role means adding one Markdown file. Adding a building means one model file and one registry entry.
  • Falls back to a plain HTML view without WebGL, and respects prefers-reduced-motion.
Screenshot of REWIND - Event Campaign Site

REWIND - Event Campaign Site

Aug 2026 - Sep 2026

The whole campaign site for The Forum's back-to-school party, from the mailing list through capped $5 guest lists to an event that sold 600+ tickets.

  • React 19
  • TypeScript
  • Vite
  • Supabase
  • PostgreSQL
  • Vercel
  • Brevo CRM
  • Meta CAPI
Read the write-up

Problem

REWIND was The Forum's back-to-school party for SFU students at Celebrities Nightclub on September 20, 2026. The venue's ticketing contract meant general sales had to go through TicketWeb, which takes a large cut of every ticket: $30 all-in. Discounted presale and friends-and-family entry were ours to run, at $5 at the door with no fees.

So the club needed one site for the whole campaign: collect a mailing list before the drop, run capped discount guest lists shared privately by promoters, switch to ticket sales the moment the drop happened, and measure which ads produced signups. I was the only person writing code, and I owned the architecture, schema, API, front end and deployment.

Design choices

  • Capacity enforced in one locked database function. Each guest list had 75 spots, and the links went out in group chats, so signups arrived in bursts. Counting and then inserting would let two people both see 74 and push the list to 76, and the club pays for every extra spot at the door. A single PL/pgSQL function locks the list's row (SELECT … FOR UPDATE), then runs the duplicate, signup-window, age, rate-limit and capacity checks and the insert while holding the lock. Different lists never block each other.
  • Postgres as the source of truth. Each signup is committed before any third party is called, and every row records whether its Brevo sync succeeded. An outage leaves a row marked for backfill instead of losing a signup. When Brevo rejected a whole contact over a phone-number conflict, the sync retries once without the phone, under a single deadline.
  • Built to absorb change. The event was rebranded from "Back 2 School" to the Y2K-themed "Rewind" after the site was built, and the poster art didn't exist yet. Key art is a swappable layer, every string and price lives in one config file, and the palette is a token set, so the rebrand landed as content changes rather than a rewrite.
  • Released in the order the campaign needed. Lists are configuration, not code: presale went live first, then general sales, then friends and family as its own pool of 75. That last one was one config entry and one migration, without touching lists already taking real traffic.
  • Retargeting and attribution. The Meta Pixel and Conversions API report visits and signups server-side, with UUID event deduplication. That let the club retarget ads at people who visited the site but didn't sign up, and see which ads produced signups.

Impact

  • Ran the full campaign end to end in production, through to an event that sold 600+ tickets, with 150 discounted guest-list registrations captured in our own database.
  • No list ever went over its cap during the signup bursts.
  • About 15,000 lines of TypeScript and 850 of SQL, with 397 tests. Some of the tests read the SQL migrations directly and fail if a database function's signature drifts from the code that calls it.
  • 91% automated background CRM sync rate and 100% server-side conversion delivery.

What I learned

  • Lay out trade-offs in plain language before anyone commits: what a choice costs, what it buys, and what I expect to happen. "There's no wrong choice, only a misinformed one."
  • Spend more time on requirements up front, and book a second meeting after a first attempt, because the real issues only surface once something exists.
  • Design the schema to capture more than I think I need. It's easy to ignore a column, and impossible to report on data that was never recorded.
Screenshot of Distributed Gym Capacity Booking API

Distributed Gym Capacity Booking API

Aug 2026

A learning project. A bouldering-gym booking API that uses Redis's atomic counters to stop two people grabbing the last spot.

  • TypeScript
  • Node.js
  • Express
  • Redis
  • PostgreSQL
  • Vitest
Read the write-up

Goal

I wanted to understand distributed-systems ideas like atomicity, race conditions and caching in a setting I actually care about: booking sessions on a wall at a bouldering gym. Each wall has a max capacity, and two people must never both get the "last spot", even if their requests arrive at almost the same instant.

Design choices

  • Atomic counters instead of database locks. Checking "is there room?" and then writing "add one more" can race. Redis's INCR is atomic, so incrementing the live headcount and reading the new value happen as one step. If the new count is over capacity, the booking is refused and the count is decremented back.
  • Two tiers of data. Live per-slot headcounts and a cached copy of each wall's max capacity live in Redis with self-expiring TTLs. Bookings, gyms and walls persist in a normalised PostgreSQL schema as the source of truth.
  • Rollback on write failure. If the Postgres insert fails after Redis lets a booking through, the Redis count is decremented so the spot isn't lost.
  • Thin routes, central errors. Services hold the logic; errors are thrown as named codes (WALL_AT_CAPACITY, GYM_NOT_FOUND) and mapped to HTTP statuses in one handler.

What I learned

  • Validated the capacity-limiting and booking logic with targeted Vitest unit tests, mocking Redis and Postgres to cover race conditions, TTL behaviour and rollback on write failure.
  • Where this would need more for production: Redis and Postgres can still drift if the app crashes at exactly the wrong moment (an outbox pattern would fix that), and a single Redis node would become Redis Cluster at scale. I wrote up these trade-offs in the repo rather than hiding them.

Text Math RPG Game

Nov 2025

A text-based RPG in Java where combat is driven by maths, built around classic object-oriented design patterns.

  • Java
  • UML
  • OOP
Read the write-up

Design choices

  • Strategy pattern for weapons. A polymorphic weapon system resolves each weapon's behaviour at runtime, which keeps the combat logic simple no matter how many weapons are added.
  • Observer pattern between the UI and the game. The game logic publishes changes and the UI listens, keeping a strict separation of concerns.
  • Refactoring legacy code. Replaced inheritance with composition, and used the null object pattern to get rid of defensive null checks.
Screenshot of SFU Course Map

SFU Course Map

Oct 2025

StormHacks 2025 winner. An interactive graph of SFU course prerequisites.

  • Next.js
  • TypeScript
  • D3.js
  • Gemini API
Read the write-up

Problem

Planning a degree at SFU means digging through prerequisite chains written as free text in course descriptions. It's hard to see which courses unlock which, or what the shortest path to an upper-division course looks like.

Design choices

  • A force-directed graph in D3.js, rendered in Next.js, so you can see prerequisite networks and follow a path by clicking through nodes.
  • An automated parsing pipeline using the Gemini API. It turns unstructured prerequisite text into structured JSON, then checks that output before it reaches the graph.

Impact

  • Won Best Use of SFU Courses API at StormHacks 2025.
  • Built and demoed live, with an architecture walkthrough for the judges, inside a 24-hour hackathon.
Screenshot of Travelytics

Travelytics

Summer 2025

A travel analytics app with real-time demographics and weather for any destination (CMPT 276).

  • React
  • JavaScript
  • REST APIs
  • Vercel
Read the write-up

Problem

Planning a trip means checking several sites for the weather, the local population and other basic facts. Travelytics puts them on one dashboard.

Design choices

  • Combines several public APIs and shows the results side by side.
  • Built as a team for the CMPT 276 (Software Engineering) final project.

Impact

  • Deployed on Vercel and live today.
Screenshot of Receipt OCR AI

Receipt OCR AI

Summer 2025

A machine-learning pipeline that pulls structured data out of photos of receipts (CMPT 310).

  • Python
  • OpenCV
  • scikit-learn
Read the write-up

Problem

Receipts are messy: crumpled, skewed, and printed in many layouts. We wanted to turn a phone photo into clean, structured fields like the store, line items and totals.

Design choices

  • OpenCV preprocessing (deskewing, thresholding, cropping) before text recognition.
  • scikit-learn models classify the recognised text into structured fields.

Impact

  • Built as the CMPT 310 (Intro to AI) term project; results are summarised in the poster.
Screenshot of Meal Fetcher

Meal Fetcher

DreamHacks 2025

DreamHacks 2025. Type in the ingredients you have and get recipes back from TheMealDB.

  • JavaScript
  • TheMealDB API
  • Vercel
Read the write-up

Problem

"What can I cook with what's in my fridge?" Recipe sites usually start from a dish, not from your ingredients.

Design choices

  • Looks up TheMealDB by ingredient and shows the matching recipes as cards.
  • Kept deliberately small so it could ship within the hackathon.

Impact

  • Shipped at DreamHacks 2025 and deployed on Vercel.

Discord LLM Bot

A Discord bot that answers in chat using a large language model.

  • TODO

Uptime Infrastructure

Self-hosted monitoring for my services, built with Docker Compose, Caddy and Uptime Kuma.

  • Docker Compose
  • Caddy
  • Uptime Kuma
  • Linux

Experience

  1. Jun 2026 - PresentActive

    VP of Technology

    The Forum · Burnaby, BC

    Own The Forum's technology: production hosting, the marketing data pipeline, and the tools the executive team relies on to recruit and run events.

    What I did

    • Deployed and administer the organisation's production sites on AWS Lightsail, including Linux server setup and deployments.
    • Built a retargeting funnel for visitors who didn't register, using GA4, Brevo CRM and the Meta Conversions API. Marketing can now attribute landing-page traffic by source.
    • Shipped an MVP hiring page under a tight deadline. It handled 75+ applications.
    • Built REWIND, the campaign site and capped $5 guest lists for an event that sold 600+ tickets, as the only developer.
  2. Jun 2026 - PresentActive

    Director of Web Development

    MISA (Management Information Systems Association) · Burnaby, BC

    Stand up MISA's new Web Development division. The first deliverable is an event registration and payment platform.

    What I did

    • Co-leading the team building the platform with Vite, Express and Stripe, self-hosted on a DigitalOcean Droplet.
    • Defined the division's core stack (Vite, Express, PostgreSQL) and its software architecture.
    • Designed technical hiring rubrics and onboarding docs for an incoming team of 4-5 web developers, together with the Director of Software Systems.
  3. Sep 2025 - May 2026Completed

    Web Developer Coordinator

    The Forum · Burnaby, BC

    Maintain and extend The Forum's web platforms through the 2025-26 year.

    What I did

    • Drove 250+ new sign-ups with interactive 3D web features built in Next.js and three.js.
  4. Dec 2025 - PresentActive

    AP Computer Science Instructor (CSA & CSP)

    StudyPug · Richmond, BC

    Teach AP Computer Science A (Java) and AP Computer Science Principles (Python) to high-school students.

    What I did

    • Deliver technical lectures that break hard concepts like recursion, polymorphism and Big-O analysis into digestible modules.