Torus is a Tauri + TypeScript desktop reimplementation of the Emacs Lisp torus game from the newbiemacs project.
- Mac App Store: T@rus
- Latest release page: Torus Releases
- Direct installers are attached in each release asset list.
- Emacs-style ASCII torus gameplay with animated falling tori.
- Pole movement under the box, with pick/drop mechanics.
- Three difficulties:
1: Normal2: Half-glazed + Rotate3: Half-glazed + Flip
- Theme switching and compact single-screen desktop layout.
- Custom theme editor (click theme chip): adjust torus/text/glaze/glow colors, sample, and save locally.
- Custom one-shot
Skills(create/run/edit/delete directional sequences, including edge-aware dynamic pair(/)). GLOBAL TOP 10,DAILY CHALLENGE TOP 10, andPERSONAL TOP 10scoreboard views.- Own records are marked with
Metag inGLOBALandDAILY. - Daily streak badge system (
2^0to2^9) based on successful consecutive Daily submissions (UTC, strict reset). - Badge tooltip on hover (current streak, best streak, next badge progress).
GLOBALTop 3 trophy badges (#1,#2,#3) with animated highlight.- Click a score row to slide open used skill details (skill name + command).
- Import skills from expanded
GLOBAL/DAILYrecords directly into personal skill set. Keyscard supports two pages: basic controls and current personal skill hotkeys/sequences.- Optional online score submission (Supabase).
- Per-install UUID in Tauri backend: one online record per device, updated only when score is better.
- Local fallback cache if network/Supabase is unavailable.
- Move:
Arrow keysorj/l/i/k - New game:
1 - Resume:
2 - Pause:
3 - Reset:
4 - Theme:
5 - Theme custom editor: click the
Theme: ...chip at top-right (Applyfor preview,Savefor persistence) - Hover badge icon: show streak/rank details
- Skills:
6 - Toggle scoreboard:
7 - Key card cycle:
8(Page 1 -> Page 2 -> Hide -> Page 1) - Difficulty cycle:
9(1 -> 2 -> 3 -> 1) - Skill hotkeys: set per skill in
Skillsmodal by pressing a key (duplicate registrations are blocked)
Keyboard input uses both event.key and event.code, so game controls still work in non-English IME layouts (for example Korean input mode).
- Frontend: Vite + TypeScript
- Desktop runtime: Tauri v2
- Backend commands: Rust (Tauri
invoke) - Online ranking: Supabase REST API (
scorestable) - Local persistence: browser localStorage + Tauri app data cache
- Node.js 20+
- Rust toolchain (stable)
- Tauri system prerequisites for macOS
npm install
cp .env.example .env
# Fill .env values
npm run tauri devVITE_SUPABASE_URL=https://YOUR_PROJECT_ID.supabase.co
VITE_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_PUBLIC_KEYIf these are not set, online sync is disabled and scoreboard works in local-only mode.
- Create a Supabase project.
- Open SQL Editor and run
/supabase/schema.sql. - Copy project URL and anon key into
.env. - Deploy Edge Function
/supabase/functions/verify-score. - Set function secret
SUPABASE_SERVICE_ROLE_KEY. - Start the app and submit a score.
Example:
supabase functions deploy verify-score --no-verify-jwt
supabase secrets set SUPABASE_SERVICE_ROLE_KEY=YOUR_SERVICE_ROLE_KEY- Edge Function names are immutable in practice. To "rename", deploy a new function name.
- Recommended rollout:
- Deploy
verify-score - Release client versions that call
verify-score - Keep
verify-daily-scoretemporarily for older clients - Remove
verify-daily-scoreafter old versions are no longer active
- Deploy
The schema includes:
scores.player_name(text)scores.client_uuid(text)scores.score(integer)scores.level(integer)scores.skill_usage(jsonb, default[])scores.mode(text:classic|daily)scores.challenge_key(text:classicorYYYY-MM-DD)scores.attempts_used(integer: daily only,1..3)scores.daily_has_submission(boolean: daily ranking visibility)scores.active_attempt_token(text: active daily attempt token)scores.created_at(timestamptz)daily_streak_states.client_uuid(text)daily_streak_states.current_streak(integer)daily_streak_states.max_streak(integer)daily_streak_states.last_submission_key(text:YYYY-MM-DDornull)submit_daily_score(...)RPC function (server-enforced daily attempts)verify-scoreEdge Function (server replay verification for Global and Daily)
RLS behavior:
- Direct
insert/updateonscoresfrom anon/authenticated clients is blocked. - Global/Daily writes are accepted only after Edge Function replay verification.
submit_global_score(...)andsubmit_daily_score(...)RPC execute permissions are restricted toservice_role.
- It is safe and recommended to commit
/supabase/schema.sqland/supabase/functions/**. - Never commit secrets (
SUPABASE_SERVICE_ROLE_KEY, DB password, JWT secrets, private keys,.env*values). - Keep runtime secrets only in Supabase secrets / CI secrets / local untracked env files.
Ranking query order is:
score DESClevel DESCcreated_at DESC
Default fetch size is top 10.
- Personal records are always stored locally.
- Online submission is optional.
- Submitted scores include used skill metadata (
skill_usage). - Tauri backend generates and stores a UUID at first run (
device-uuid-v1.txtin app data dir). - Classic mode online submission:
- Uses a single row per owner via
(mode='classic', challenge_key='classic', client_uuid). - If row does not exist: insert.
- If row exists: update only if new score is better (or same score with higher level).
- Uses a single row per owner via
- Daily Challenge mode online submission:
- Uses
(mode='daily', challenge_key=UTC date, client_uuid). - Daily runs are auto-submitted (no opt-out).
- Server RPC enforces maximum 3 attempts per UTC day.
- Client submits replay proof (seed + timed move log + final state).
- Supabase Edge Function re-simulates the run and rejects mismatched score/level/time.
attempts_usedincrements even when score does not improve.- When daily best improves, that run is also auto-submitted to classic Global (same best-upsert rule).
scoreskeeps only today's Daily rows.- Daily streak state is kept in
daily_streak_states(current_streak,max_streak,last_submission_key). - Badge tier is derived from stored
max_streak.
- Uses
This prevents duplicate classic entries and makes daily attempt limits tamper-resistant.
npm run build
cargo check --manifest-path src-tauri/Cargo.tomlsrc/main.ts: app bootstrap and UI/event wiringsrc/game.ts: game loop and mechanicssrc/scoreboard.ts: local/global scoreboard store abstractionsrc/ui/layout.ts: DOM template and bindingssrc/ui/renderer.ts: rendering logic (playfield, HUD, cards)src/ui/theme.ts: theme handlingsrc-tauri/src/scoreboard.rs: backend fetch/submit/cache/UUID logicsupabase/schema.sql: DB schema and RLS policies
