Synhax is a real-time UI battle platform for HTML and CSS. Players compete by recreating a visual target, while the app tracks progress with live diff scoring and synced state.
This repository contains the full app: frontend, auth, realtime sync layer, API endpoints, and database schema.
- Hosts design recreation challenges called targets (code, image, or video based)
- Lets users run solo or 2-player timed battles
- Tracks each submission as a hax (user HTML + CSS)
- Computes a live diff score (0-100) against the target
- Supports a lobby -> battle -> recap game flow
- Includes admin tools for managing users, targets, battles, and hax records
- Player: joins battles, edits code, submits results
- Referee/Host: manages lobby settings, starts battles, controls battle flow
- Admin (
syntax): manages private targets, admin pages, and media upload tooling
- SOLO: one player against the timer
- TIMED_MATCH: two players in a head-to-head match
- Win condition: currently optimized for
FIRST_TO_PERFECT(first to 100% diff score)
- A user starts from a target card in the dashboard.
- A battle record, participant row, and hax row are created.
- In the lobby, the host configures mode, timing, visibility, and lock-in.
- During battle, each player output is rendered in a sandboxed iframe.
- A diff engine compares player output to the target and persists score updates.
- On completion, users can view recap data and target ratings.
Synhax uses Zero for synced queries/mutations and Drizzle/Postgres for storage.
- Client sync entry:
src/lib/zero.svelte.ts - Read layer:
src/lib/queries.ts - Write layer:
src/lib/mutators.ts - Query endpoint:
src/routes/api/query/+server.ts - Mutation endpoint:
src/routes/api/mutate/+server.ts - Database schema:
src/db/schema.ts
Zero context is passed from authenticated request state:
- Query context:
{ userID, userRole } - Mutation context:
{ userID, userRole }
Permissions are enforced in query/mutator definitions (not with Zero
definePermissions).
The platform is built around the browser File System Access API.
- Players grant access to a local folder
- Synhax creates/loads battle files (
index.html,styles.css) - Local file changes are polled and synced into
hax+hax_history
Important:
- Chrome/Edge-style File System Access support is required
httpsorlocalhostis required for file system permissions
Implementation: src/lib/state/FileState.svelte.ts
CODE: structured target HTML/CSS (with optional starter code)IMAGE: static visual referenceVIDEO: video reference
Admin target creation/editing lives at:
src/routes/(style)/(menu)/admin/targets/TargetForm.svelte
Battle previews support Tailwind CSS v4 from CDN in sandboxed output.
- Add
@import "tailwindcss";in battle CSS to enable it - Tailwind is injected only when that import is detected
Implementation: src/utils/code.ts
- SvelteKit 2 + Svelte 5
- Zero (
@rocicorp/zero) +zero-svelte - Drizzle ORM + Postgres
- Better Auth (GitHub OAuth + JWT/bearer support)
- Cloudflare deployment target (Workers + Hyperdrive support)
- Sentry client instrumentation with telemetry tunnel endpoint
/- login/start screen/dashboard- targets + your battles/lobby/:id- pre-battle setup and lock-in/battle/:id/code- active coding/battle view/battle/:id/code/breakout- detached output window/ref/:id- referee control view/recap/:id- post-battle recap/history- player battle history/settings- local folder + account controls/admin/*- admin-only management screens
These are the key variables used by this app:
ZERO_UPSTREAM_DB- Postgres connection string (runtime + Drizzle CLI)PUBLIC_SERVER- Zero server URL used by the client sync layerGITHUB_CLIENT_ID- GitHub OAuth client idGITHUB_CLIENT_SECRET- GitHub OAuth client secretIV_API_KEY- API key for admin media upload proxy endpoints
Cloudflare-specific:
HYPERDRIVEbinding can provide the runtime DB connection string
- Install dependencies:
pnpm install-
Configure environment variables (
.envor platform env). -
Push schema to Postgres:
pnpm push- Start Zero cache dev server (terminal 1):
pnpm sync- Start app dev server on port
7777(terminal 2):
pnpm dev- Open
http://localhost:7777
pnpm dev- run SvelteKit dev serverpnpm sync- run Zero cache dev serverpnpm push- push Drizzle schema changes to DBpnpm sync-generate- regenerate Zero schema from Drizzle schemapnpm check- typecheck withsvelte-checkpnpm lint- Prettier checkpnpm format- Prettier writepnpm build- production build
Core tables:
usertargetsbattlesbattle_participantshaxhax_historyratingsbattle_votesawards
Schema source: src/db/schema.ts
- App routes are client-rendered (
ssr = false) - Admin access is role-based (
syntax) - Tasks in this repo are tracked with Dex (
.dex/)