Underground 2 Revived
Play Need for Speed: Underground 2 (2004) in Chrome and Firefox with WebGPU graphics, Web Audio, up to 120 fps widescreen, controller support, free roam and Online races with friends. Fan project, not
전 세계 최대 온라인 퍼즐인 1,000,000조각 퍼즐을 실시간으로 함께 맞추며, 다운로드·계정 없이 무료로 즐깁니다
The world's largest online jigsaw puzzle: 1,000,000 pieces on one shared canvas, solved together in real time, free, with no download or account needed.
게임TypeScript · VueGitHub @MillionPiecePuzzle온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기The world's largest online jigsaw puzzle. One million pieces on a single shared board.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A community-built online jigsaw puzzle: 1,000,000 pieces on a single shared canvas, all shuffled from the start, solved together in real time. A long-form, non-commercial, open-source event with no time limit.
v1.4.2 is out: production serves the 1,000,000-piece earth-mosaic board ahead of the public event. Work is tracked in ROADMAP.md.README 전문 보기 ↗A community-built online jigsaw puzzle: 1,000,000 pieces on a single shared canvas, all shuffled from the start, solved together in real time. A long-form, non-commercial, open-source event with no time limit.
Play at app.millionpiecepuzzle.com
v1.4.2 is out: production serves the 1,000,000-piece earth-mosaic board ahead of the public event. Work is tracked in ROADMAP.md.
Every player, guest or signed in, connects over a single WebSocket path. Past a global connection cap an admission queue holds new arrivals in a first-in-first-out wait, so the server sheds load into an orderly queue instead of accepting connections until it falls over.
A restart is announced rather than suffered: the server tells every connected player it is going down before it drops the sockets, and the app switches to a maintenance screen (served by Cloudflare Pages, so it survives the backend being gone) that reloads itself back into the board once the server answers again.
Piece events are tiered by cost:
| Event | Scope |
|---|---|
| Drag | Broadcast to viewport-neighbor clients, never persisted |
| Drop | Redis write, viewport broadcast |
| Snap | Redis write, Mongo log, global broadcast |
Pieces belong to clusters. Every piece starts as its own cluster; when two pieces of compatible neighboring positions meet at the correct relative offset, their clusters merge and their relative positions freeze. Grabbing any piece grabs its whole cluster, and the wire format carries one absolute position plus a group id rather than N piece positions.
The puzzle frame is the anchor. A cluster locks permanently when a human drop brings its origin within snap tolerance of the frame origin, or when it merges with an already locked cluster. Locked pieces never move again: no undo, no griefing.
Piece geometry is neither stored nor sent. Bezier silhouettes and canonical offsets derive from a server-only generation seed and are baked into each pre-masked tile by the slicer. The client renders from those tiles plus per-piece offsets, so it never holds anything a solver could use.
The source image carries no AI-generated content, a community commitment.
@auth/express, Google), database sessions, internal per-IP login rate limitingpackages/
shared/ # Shared TypeScript types (WS messages, piece and user schemas)
frontend/ # Vue + PixiJS + OpenSeadragon
server/ # Node + WebSocket + Redis/Mongo handlers
load-test/ # WS load/soak harness (bots + admission gate)
git clone https://github.com/MillionPiecePuzzle/million-piece-puzzle.git
cd million-piece-puzzle
npm install
cp .env.example .env
docker compose up --build -d
Frontend on http://localhost:5173, WebSocket server on ws://localhost:8080/, anonymous landing data on http://localhost:8080/landing, http://localhost:8080/live and http://localhost:8080/interested.
.env is gitignored and never committed. Fill it in before bringing the stack up:
AUTH_SECRET, a 32+ character random string, e.g. openssl rand -hex 32. Without it the /auth routes fail, so the SPA cannot read its session back and mints a fresh guest on every visit.AUTH_GOOGLE_ID and AUTH_GOOGLE_SECRET, from a Google OAuth client whose authorized redirect URI is http://localhost:8080/auth/callback/google. Without them the server still runs and guest play works end to end; only Google sign-in and guest claiming are unavailable.The non-secret auth config (AUTH_URL, AUTH_COOKIE_DOMAIN, MPP_APP_ORIGIN) has local defaults.
| Command | Purpose |
|---|---|
npm run build | Build shared, then server, then frontend |
npm run typecheck | Typecheck every workspace |
npm test | Run the Vitest suite |
npm run lint | ESLint over the monorepo |
npm run format | Prettier write |
npm run dev:frontend | Vite dev server, outside Docker |
npm run dev:server | Server in watch mode, outside Docker |
The image pipeline carries its own scripts (slice, mosaic, upload, validate:generation); see package.json.
The wire protocol (v10), schema, and gameplay are frozen for the public event. Code contributions are welcome, but please open an issue first to discuss the change before sending a pull request.
CLAUDE.md holds the working conventions, ROADMAP.md the tasks and their exit criteria, and DECISIONS.md the non-obvious trade-offs along with what would make each worth revisiting.
Bug reports and feedback go to GitHub Issues or the Discord.
MIT. See LICENSE.