Upload a face. AI turns it into pixel art. Smash it. · Léelo en español
Made by Marabunta Labs · play it at https://face-brawl.vercel.app
A browser fighting game where you are the fighter: you give it a photo (or take one with your webcam or phone) and your face is detected, cut out, turned into pixel art and glued on a boxer's body. Every hit you take then damages the real photo: bruises, swollen eyes, cuts with blood. 1P vs CPU, 2 players, and an Arcade ladder. Everything runs in the browser: no server, no build step, your photos never leave your device.
npm start # or: python3 -m http.server 8080
# open http://localhost:8080
Any static file server works (the game is plain ES modules). The first time you need an internet connection (MediaPipe model and runtime are loaded from a CDN); without it the face editor opens with default features.
- Face pipeline – MediaPipe Face Landmarker (468 points) finds the face, you can fine-tune it in an editor;
the background is cut out along the outline (plus the hair, segmented by MediaPipe
hair_segmenter); the result is painted with 5 realistic damage stages (bruises that multiply the skin colour, swollen eyelids, bleeding cuts) and pixelated to 40×40 (48×48 with hair) with one shared palette. A "no pixelation (HD)" mode renders at 3×. - Two sprite bodies – Boxer PRO and Female boxer, headless pixel-art frames (idle, jab, cross, uppercut, kick, walk, jump, block, hit, KO, win) with your face attached at the neck. The body skin tone follows your face and can be changed by hand (6 tones).
- Moves – punch chain jab → cross → uppercut (press punch again; the uppercut needs the cross to land), forward + punch = cross, kick, sweep (block + kick), grab (block + punch), dash (double tap), parry, air attacks, and the SUPER rush: with a full bar the fighter charges, dashes at the rival and traps them in a flurry of punches ending in an uppercut.
- Modes – 1 player vs CPU (easy / normal / hard), 2 players (in both, each fighter can be a photo, a saved/sample character picked from the face chips, or a 🎲 random one), Arcade ladder (your saved faces and coloured clones; until you save some, it uses the sample rivals in
assets/demo-rivals/). Saved characters, rankings and stats are kept in the browser (localStorage). - Stages – 6 built-in stages (4 procedural, 2 AI-made pixel-art backdrops) or your own photo turned into a stage (fixed floor, adjustable zoom and framing).
- Phone as controller or camera – scan a QR code and the phone becomes a gamepad (
pad.html) or sends photos of faces / places (photo.html). Direct WebRTC connection (PeerJS); internet is only needed to pair. - Languages – English and Spanish, picked from the browser language and changeable in ⚙ Settings. Adding a language takes one file (see below).
- Replay recording (share or save the last fight as video), remappable keys, music (WebAudio, no files).
| P1 | P2 | |
|---|---|---|
| Move | A / D | ← / → |
| Jump / Block | W / S | ↑ / ↓ |
| Punch / Kick / Super | F / G / H | K / L / J |
Against the CPU both key sets work. Pause: Esc or P. Keys can be remapped in ⚙ Settings.
"📱 Phone as controller" shows a QR (one per player in 2P mode); the 📱 PHONE buttons next to each face slot and the
stage card show a QR that opens photo.html.
- Deployed online (HTTPS): the phone does not need to be on the same Wi-Fi. The two devices find each other
through the free public PeerJS broker and then talk directly (WebRTC), so it works from mobile data too. HTTPS also
gives the phone a live camera preview with a face guide. In rare cases (strict mobile carriers / corporate networks)
a direct connection cannot be opened; PeerJS's public service also offers relay (TURN) servers as a fallback, and if it
still fails try the same Wi-Fi or run your own PeerJS server (the
new Peer()calls injs/remote.js,js/pad.js,js/photo.js). - Running on
localhost: the phone cannot reach "localhost", so type your computer's LAN IP (e.g.192.168.1.20:8080) in the dialog; both devices must be on the same Wi-Fi and the phone opens its camera app (live preview needs HTTPS).
The game is a static site (no build step). Publish the repository root anywhere that serves static files over HTTPS.
Vercel (what this repo is set up for): import the GitHub repository, Framework Preset: Other, leave the build command
empty and the output directory as the root. vercel.json adds clean URLs, cache and security headers (camera allowed for
the page itself). Other hosts work too: GitHub Pages (Settings → Pages → deploy from main / root, .nojekyll included),
Netlify, Cloudflare Pages.
Search engines (SEO): index.html ships the title, description, canonical URL, Open Graph / Twitter cards, JSON-LD
(VideoGame) and a crawlable description; sitemap.xml, robots.txt, a web manifest and the icons / social image
(assets/social/) are included. The files contain the placeholder address https://face-brawl.vercel.app: once you know
the real URL run
node scripts/set-site-url.mjs https://your-real-domain.com
and commit. Then add the site in Google Search Console and submit sitemap.xml. HTTPS is needed for the phone camera
preview and the webcam.
The repository includes a few sample photos of public figures (test-faces/, and their pixel-art versions in
assets/menu-faces/ and assets/demo-rivals/) for demonstration. Delete those folders if you prefer: the title screen
then falls back to built-in cartoon faces.
Photos are processed in the browser and never uploaded (details in legal.html). Code and artwork are MIT
licensed (LICENSE); third-party components are listed in
THIRD_PARTY_NOTICES.md.
index.html, style.css game shell and styles
pad.html, photo.html phone pages (gamepad / photo sender)
anim-editor.html developer lab (see below)
legal.html privacy and terms
js/
main.js UI glue: slots, menus, dialogs, arcade flow
menu.js, menufx.js screen navigation and animated synthwave background
game.js fight engine: attacks, AI, HUD, effects, replay
sprite_bodies.js loads the sprite bodies, finds the neck, tints skin
face.js, landmarks.js face detection → cutout → pixel art
framer.js, cutout.js feature editor and background cutout
damage.js, expr.js damage stages and facial expressions
stage.js, stages.js photo → stage, built-in stages and floors
roster.js, rosterui.js saved characters and stats
remote.js, pad.js, photo.js phone connection (PeerJS)
i18n.js, locales/ translations (en.js is the reference)
rig.js, body.js, outfits.js, pixelbody.js, characters.js
procedural (non-sprite) bodies, kept for future use
assets/
sprites/boxer, sprites/female PNG frames of each body (transparent, headless)
stages/ AI-generated backdrops
- Copy
js/locales/en.jstojs/locales/xx.jsand translate the values (keep the keys and{placeholders}). - Add one line to
LANGSinjs/i18n.js:xx: { name: 'Native name', load: async () => (await import('./locales/xx.js')).default }.
Missing keys fall back to English. The picker in ⚙ Settings lists every language in LANGS.
anim-editor.html runs the real engine on a page: live fights with pause / slow motion, expressions × damage stages,
wounds, clones, stages, the face pipeline and the body animation sheet. Add your own sample photos in
test-faces/ and list their file names in test-faces/faces.json, or upload one in the page.
?body=female opens it with the female body. ?classic on the game URL shows every menu screen stacked and without
animations (used by automated tests).
- Sprites and stage backdrops were generated with AI image models and cleaned up with scripts (transparent background, slicing, neck detection); the face and damage code is original.
- Face models: MediaPipe (Face Landmarker and Hair Segmenter), loaded from a CDN.
- Phone pairing: PeerJS, QR codes: qrcode, both from a CDN.
- Font: Press Start 2P (Google Fonts).
- The sample faces are photos of public figures used for demonstration only; no endorsement or affiliation is implied. If you are pictured and want your photo removed, open an issue and it will be taken down.