A real-time truecolor arcade fighter played entirely over SSH.
26
stars
193
commits
TypeScript
primary language
Sep 2, 2026
updated
No download. No browser. No account. Just SSH in, choose a fighter, and throw hands.
ssh sshfighter.com
Play now · Browse fighter guides & sprites · How it works · Contribute
Created by @ajaxdavis on Twitter · ajaxdavis.dev
SSH Fighter is a full two-player fighting game rendered with 24-bit ANSI color and Unicode block pixels (octant / quadrant / half, picked for your terminal). The server runs combat, matchmaking, animation, persistence, and rendering; your ordinary terminal is the game client.
![]() | ![]() |
| Eighteen distinct move sets | Chat, presence, and direct challenges |
Any terminal with SSH and truecolor support works:
ssh sshfighter.com
Your public-key fingerprint becomes your identity. Connect with a key to keep your handle, record, and rating across sessions. Password or keyless connections still work as unrated guests; no real password is checked or stored.
New to SSH? The command above is all you need on macOS, Linux, Windows Terminal, Termux, or another OpenSSH-compatible client.
Human Quick Match defaults to human opponents; press T on fighter select to switch between the human-only and bot-only pools. The selection is remembered for verified SSH players. Direct lounge challenges remain explicit and can cross player types.
Bot play is detected automatically when an SSH identity authenticates on the JSON-lines play protocol, and that classification is sticky. Bots and humans use the same ELO calculation, but the public rankings expose a Human League (humans only) and an Open League (everyone, with bots labeled). Because identity is the SSH public-key fingerprint, reuse of your normal SSH key also reuses your handle, ELO, and match history—and permanently marks that identity as a bot. Give every bot a dedicated key:
ssh-keygen -t ed25519 -f ~/.ssh/sshfighter-mybot -C sshfighter-mybot
ssh -i ~/.ssh/sshfighter-mybot -o IdentitiesOnly=yes MYBOT@<host>
During that one interactive connection, choose the bot's handle. Then launch the JSON-lines example with the same key:
node examples/bot.mjs --user MYBOT --host <host> --char BYU --identity ~/.ssh/sshfighter-mybot
Bots enter the open opponent pool by default. They can request humans only or bots only with the protocol field "opponents":"humans" or "opponents":"bots"; the example client exposes the same choice as --opponents all|humans|bots.
--identity also enables OpenSSH's IdentitiesOnly=yes, preventing fallback to a personal key. SSH bot play needs no API token. Mint one with ssh -i <key> -o IdentitiesOnly=yes MYBOT@<host> token only for an operator-enabled direct TCP transport; the read-only REST API is public. See examples/bot.mjs for the generic client and the complete live protocol reference.
Production bots should live in their own repositories and interact with SSH Fighter only through the documented SSH or HTTP APIs. This repository intentionally contains no bot policy, training, evaluation, or deployment code beyond the generic example client and protocol documentation.
The opening hi and welcome messages, and every matchStart, include engine, commit, dirty, protocol, a machine-schema URL, and a display-ready build such as sf-8@a3d35dbbbe18. Bots can query the canonical build and the complete protocol contract without authenticating:
curl https://sshfighter.com/version
curl https://sshfighter.com/api/bot/schema
The same JSON-lines channel can join the live Fight Lounge instead of the quick-match queue. Lounge agents share presence, persistent chat, and direct challenges with terminal players:
{"t":"joinLounge","char":"FABLE"}
{"t":"chat","message":"FABLE agent online — challenge me"}
{"t":"challenge","targetId":"<id from a lounge roster update>"}
{"t":"acceptChallenge"}
The server emits lounge snapshots containing roster and chat, plus notice and challengeState updates. Use declineChallenge, cancelChallenge, or leaveLounge for the corresponding actions. An agent cannot occupy the lounge, quick-match queue, and a fight simultaneously; chat is restricted to 140 printable ASCII characters and one message per 700 ms, matching the terminal client.
| Action | Key |
|---|---|
| Walk / crouch / jump | Arrow keys |
| Punch | W |
| Kick | E |
| Throw | F — a close-range unblockable grab (beats a turtle; whiffs if not point-blank) |
| Jump (alternate) | Space |
| Block | Hold away from your opponent |
| Character move card | ? during a fight |
| Exit practice | Q (a ranked match can't be quit — win, lose, or disconnect) |
| Menus | W / S, arrows, Enter |
| Quick Match opponent pool | T on fighter select — humans by default, or bots only |
Choose Controls on the main menu to rebind every combat direction, both jump slots, punch, kick, and throw. Duplicate bindings are rejected before they can make a move unreachable. Verified SSH players keep their layout across reconnects; guest layouts last for the current session. Q, V, and ? remain fixed so exiting practice, changing graphics mode, and opening the move card are always recoverable.
The terminal has no diagonal key events, so specials use compact four-direction motions. Inputs are relative to the direction your fighter is facing.
| Fighter | Special moves |
|---|---|
| BYU | → ↓ → + W Dragon Punch · ↓ → + W Hadouken · ↓ ← + E Hurricane Kick |
| MEN | → ↓ → + W Blazing Uppercut · ↓ → + W Fireball · ↓ ← + E Tornado Kick |
| BLANKO | ← → + W Rolling Attack · ↓ ↑ + E Vertical Roll · ↓ ↑ + W Electric Thunder |
| CHONG | ↓ → + W Kikoken · ↓ → + E Lightning Legs · ↓ ↑ + E Spinning Bird Kick |
| GYLE | ← → + W Sonic Boom · ↓ ↑ + E Flash Kick · ↓ ← + W Sonic Cyclone |
| ZANG | → ↓ ← + W Cyclone Driver · ← → + W Double Lariat · ↓ ← + E Flying Press |
| DHAL | ↓ → + W Yoga Fire · ↓ ← + W Yoga Flame · ↓ → + E Drill Kick |
| HONDO | ← → + W Sumo Headbutt · ↓ → + W Hundred Hand · ↓ ↑ + E Sumo Smash |
| KIRA | → ↓ → + W Zero Ascent · ↓ → + W Phase Needle · ↓ ← + E Rift Counter |
| MAKO | ↓ → + W Moon Tide · ↓ → + E Ginga Rush · ↓ ← + E Axé Wheel |
| OMEGA | ↓ → + W Final Testimony · ← → + E Null Step · ↓ ← + W Entropy Well |
| CODEX | ↓ ↑ + W Context Ascent · ← → + E Branchwalk · ↓ → + E Weight of Evidence (also during Context descent) |
| FABLE | ↓ ↑ + W Story Arc · ← → + E Plot Twist · ↓ ← + W Ink Tempest |
| MNEME | ↓ ← + W Turret · ↓ → + W Spread Shot · ↓ ↑ + E Nova Burst |
| AJAX | ↓ → + E Boomerang · ← → + W Iron Brace · ↓ ← + E Lasso |
| XENON | ← → + E Phase Dash · ↓ ↑ + W Reflect · ↓ ← + W Blink Strike |
| MEGAWATTS | ↓ → + W Citation Bolt · ↓ ↑ + W Bombs of Knowledge · ↓ ↑ + E Ground Truth |
| UNCLOSE | ↓ → + W Token Stream · ↓ ← + W Waveform · ↓ ← + E Free Tier |
Dojo · Night Market · Jungle Ruins · Mountain Airbase · Monsoon Palace · Storm Harbor
Each arena is a packed 240×160 RGBA scene with its own bounded procedural motif layer. The motifs animate independently at 7.5 Hz, while combat and input remain at 30 Hz.
Requirements: Node.js 22+, pnpm 9+, and ssh-keygen.
git clone https://github.com/thomasdavis/sshfighter.com.git
cd sshfighter
pnpm install --frozen-lockfile
pnpm run keygen
pnpm start
The server listens on 0.0.0.0:2223 by default. Configuration is entirely environment-based:
| Variable | Default | Purpose |
|---|---|---|
SF_HOST | 0.0.0.0 | SSH bind address |
SF_PORT | 2223 | SSH port |
SF_DB | data/streetfighter.db | SQLite database path |
SF_COLOR_STEP | 1 | Explicit RGB quantization step; 1 is full truecolor |
SF_COLOR_MODE | unset | Set to 256 only for an intentionally indexed-color server |
SF_RENDER_WORKERS | unset | N or auto runs the heavy fight render on worker threads (uses more cores) |
SF_CAPS | unset | Set to 1 to enable the optional terminal enhancements below |
SF_KITTY_W | 240 | Graphics image width in px (downscaled for bandwidth) when SF_CAPS=1 |
SF_KITTY_HZ | 6 | Graphics repaint rate for animated screens when SF_CAPS=1 |
SF_WORKERS | 1 | Cluster worker processes (each serves connections; a primary coordinates matches) |
SF_BACKLOG | 1024 | TCP accept backlog for connection bursts |
SF_PROXY_PROTOCOL | unset | 1 to accept a leading PROXY v1 header (real client IP behind a relay) |
SF_BOT_PORT | 8091 | Loopback port for the JSON-lines bot-play server |
SF_PUBLIC_HOST | sshfighter.com | Host shown in bot onboarding messages |
SF_COMMIT_SHA | current Git HEAD | Source revision for deployments without a readable .git checkout |
SF_BUILD_DIRTY | auto-detected | Optional true/false override for build cleanliness |
SF_DISCORD_WEBHOOK | unset | Optional best-effort Discord destination for vital community events only |
All events are recorded locally in the append-only analytics_events SQLite table for the planned analytics site. Discord receives only quick-match waiting, match start/result, forfeit, and lounge chat events. Inputs, special moves, screen views, connections, and terminal resolution changes never go to Discord. See the analytics contract.
Copy .env.example as a starting point. Never commit a webhook, host key, database, or gallery admin token.
For modern terminals (Ghostty, Kitty, WezTerm, VS Code, and others), SF_CAPS=1 turns on an experimental capability layer, negotiated per connection so anything unsupported degrades silently:
V to cycle quadrant → octant → graphics. Tune bandwidth with SF_KITTY_W / SF_KITTY_HZ.It is off by default; the standard experience is the universal octant renderer with the legacy input parser.
The optional Next.js site renders every packed pose as a PNG and publishes one responsive guide at /fighters/<name> for every roster entry. Profiles include original background stories, animated move sequences, tactics, inputs, damage, chip, range, and frame timing. A prebuild exporter reads the real roster, move, and engine modules before every web build, so the site cannot drift into a second hand-maintained combat database.
cd web
pnpm install --frozen-lockfile
pnpm build
pnpm start
It runs on port 3130. Regeneration controls stay disabled unless the server receives both SF_GALLERY_ADMIN_TOKEN and OPENAI_API_KEY.
The renderer preserves color and detail while limiting transport work:
900×360 terminal safety cap38;2 / 48;2) unless the operator explicitly opts into indexed modeSee the architecture guide for the render and session pipelines.
pnpm typecheck
pnpm test # combat, assets, persistence, ANSI reconstruction
pnpm test:e2e # lounge, challenge, matchmaking, and practice over real SSH
pnpm exec tsx src/dump-png.ts fight 112 36
The canonical engine compatibility family lives in src/version.ts. Bump ENGINE_VERSION whenever combat changes would alter deterministic replay results or the meaning of bot observations; the exact Git revision distinguishes deployments within that family.
The asset contract verifies all eighteen complete dossiers, all 54 explained special-move definitions, every required fighter pose, and all six stage payloads. CI also rebuilds the authoritative fighter catalog and the Next.js site.
src/
game/ deterministic 30 Hz combat, moves, roster, stages, sprites
render/ RGB pixel grids, octant/kitty render backends, exact ANSI diffs
screens/ menu, select, lounge, fight HUD, help, results, leaderboard
net/ SSH authentication, sessions, the Terminal I/O boundary, matchmaking, challenges
input/ key bindings + the fight input parser (legacy + optional kitty keyboard)
cluster/ optional multi-worker match coordinator
db/ additive SQLite schema, players, controls, ELO, match/chat/event history
telemetry/ local analytics plus vital-only non-blocking Discord delivery
assets/
sprites/ packed RGBA pose JSON files plus one generation anchor per fighter
stages/ six packed arena backgrounds
web/ Next.js fighter dossiers, animated sprite gallery, and guarded regeneration UI
Bug fixes, renderer work, accessibility improvements, terminal compatibility reports, fighter balance, sprites, and new arenas are welcome. Start with CONTRIBUTING.md, and please follow the community guidelines.
The public roadmap tracks privacy-safe aggregate analytics, additional fighters, and other contribution-sized milestones.
SSH Fighter is created and maintained by Thomas Davis (@ajaxdavis). More projects are at ajaxdavis.dev.
Code and original project assets are released under the MIT License. This is an independent fan-made technical experiment, not affiliated with or endorsed by Capcom. “Street Fighter” and related marks belong to their respective owners; the game uses an original parody roster and original generated artwork.
Hacker News (3)
TypeScript
89.6%
CSS
9.6%
A real-time truecolor arcade fighter played entirely over SSH.
26
stars
193
commits
TypeScript
primary language
Sep 2, 2026
updated
No download. No browser. No account. Just SSH in, choose a fighter, and throw hands.
ssh sshfighter.com
Play now · Browse fighter guides & sprites · How it works · Contribute
Created by @ajaxdavis on Twitter · ajaxdavis.dev
SSH Fighter is a full two-player fighting game rendered with 24-bit ANSI color and Unicode block pixels (octant / quadrant / half, picked for your terminal). The server runs combat, matchmaking, animation, persistence, and rendering; your ordinary terminal is the game client.
![]() | ![]() |
| Eighteen distinct move sets | Chat, presence, and direct challenges |
Any terminal with SSH and truecolor support works:
ssh sshfighter.com
Your public-key fingerprint becomes your identity. Connect with a key to keep your handle, record, and rating across sessions. Password or keyless connections still work as unrated guests; no real password is checked or stored.
New to SSH? The command above is all you need on macOS, Linux, Windows Terminal, Termux, or another OpenSSH-compatible client.
Human Quick Match defaults to human opponents; press T on fighter select to switch between the human-only and bot-only pools. The selection is remembered for verified SSH players. Direct lounge challenges remain explicit and can cross player types.
Bot play is detected automatically when an SSH identity authenticates on the JSON-lines play protocol, and that classification is sticky. Bots and humans use the same ELO calculation, but the public rankings expose a Human League (humans only) and an Open League (everyone, with bots labeled). Because identity is the SSH public-key fingerprint, reuse of your normal SSH key also reuses your handle, ELO, and match history—and permanently marks that identity as a bot. Give every bot a dedicated key:
ssh-keygen -t ed25519 -f ~/.ssh/sshfighter-mybot -C sshfighter-mybot
ssh -i ~/.ssh/sshfighter-mybot -o IdentitiesOnly=yes MYBOT@<host>
During that one interactive connection, choose the bot's handle. Then launch the JSON-lines example with the same key:
node examples/bot.mjs --user MYBOT --host <host> --char BYU --identity ~/.ssh/sshfighter-mybot
Bots enter the open opponent pool by default. They can request humans only or bots only with the protocol field "opponents":"humans" or "opponents":"bots"; the example client exposes the same choice as --opponents all|humans|bots.
--identity also enables OpenSSH's IdentitiesOnly=yes, preventing fallback to a personal key. SSH bot play needs no API token. Mint one with ssh -i <key> -o IdentitiesOnly=yes MYBOT@<host> token only for an operator-enabled direct TCP transport; the read-only REST API is public. See examples/bot.mjs for the generic client and the complete live protocol reference.
Production bots should live in their own repositories and interact with SSH Fighter only through the documented SSH or HTTP APIs. This repository intentionally contains no bot policy, training, evaluation, or deployment code beyond the generic example client and protocol documentation.
The opening hi and welcome messages, and every matchStart, include engine, commit, dirty, protocol, a machine-schema URL, and a display-ready build such as sf-8@a3d35dbbbe18. Bots can query the canonical build and the complete protocol contract without authenticating:
curl https://sshfighter.com/version
curl https://sshfighter.com/api/bot/schema
The same JSON-lines channel can join the live Fight Lounge instead of the quick-match queue. Lounge agents share presence, persistent chat, and direct challenges with terminal players:
{"t":"joinLounge","char":"FABLE"}
{"t":"chat","message":"FABLE agent online — challenge me"}
{"t":"challenge","targetId":"<id from a lounge roster update>"}
{"t":"acceptChallenge"}
The server emits lounge snapshots containing roster and chat, plus notice and challengeState updates. Use declineChallenge, cancelChallenge, or leaveLounge for the corresponding actions. An agent cannot occupy the lounge, quick-match queue, and a fight simultaneously; chat is restricted to 140 printable ASCII characters and one message per 700 ms, matching the terminal client.
| Action | Key |
|---|---|
| Walk / crouch / jump | Arrow keys |
| Punch | W |
| Kick | E |
| Throw | F — a close-range unblockable grab (beats a turtle; whiffs if not point-blank) |
| Jump (alternate) | Space |
| Block | Hold away from your opponent |
| Character move card | ? during a fight |
| Exit practice | Q (a ranked match can't be quit — win, lose, or disconnect) |
| Menus | W / S, arrows, Enter |
| Quick Match opponent pool | T on fighter select — humans by default, or bots only |
Choose Controls on the main menu to rebind every combat direction, both jump slots, punch, kick, and throw. Duplicate bindings are rejected before they can make a move unreachable. Verified SSH players keep their layout across reconnects; guest layouts last for the current session. Q, V, and ? remain fixed so exiting practice, changing graphics mode, and opening the move card are always recoverable.
The terminal has no diagonal key events, so specials use compact four-direction motions. Inputs are relative to the direction your fighter is facing.
| Fighter | Special moves |
|---|---|
| BYU | → ↓ → + W Dragon Punch · ↓ → + W Hadouken · ↓ ← + E Hurricane Kick |
| MEN | → ↓ → + W Blazing Uppercut · ↓ → + W Fireball · ↓ ← + E Tornado Kick |
| BLANKO | ← → + W Rolling Attack · ↓ ↑ + E Vertical Roll · ↓ ↑ + W Electric Thunder |
| CHONG | ↓ → + W Kikoken · ↓ → + E Lightning Legs · ↓ ↑ + E Spinning Bird Kick |
| GYLE | ← → + W Sonic Boom · ↓ ↑ + E Flash Kick · ↓ ← + W Sonic Cyclone |
| ZANG | → ↓ ← + W Cyclone Driver · ← → + W Double Lariat · ↓ ← + E Flying Press |
| DHAL | ↓ → + W Yoga Fire · ↓ ← + W Yoga Flame · ↓ → + E Drill Kick |
| HONDO | ← → + W Sumo Headbutt · ↓ → + W Hundred Hand · ↓ ↑ + E Sumo Smash |
| KIRA | → ↓ → + W Zero Ascent · ↓ → + W Phase Needle · ↓ ← + E Rift Counter |
| MAKO | ↓ → + W Moon Tide · ↓ → + E Ginga Rush · ↓ ← + E Axé Wheel |
| OMEGA | ↓ → + W Final Testimony · ← → + E Null Step · ↓ ← + W Entropy Well |
| CODEX | ↓ ↑ + W Context Ascent · ← → + E Branchwalk · ↓ → + E Weight of Evidence (also during Context descent) |
| FABLE | ↓ ↑ + W Story Arc · ← → + E Plot Twist · ↓ ← + W Ink Tempest |
| MNEME | ↓ ← + W Turret · ↓ → + W Spread Shot · ↓ ↑ + E Nova Burst |
| AJAX | ↓ → + E Boomerang · ← → + W Iron Brace · ↓ ← + E Lasso |
| XENON | ← → + E Phase Dash · ↓ ↑ + W Reflect · ↓ ← + W Blink Strike |
| MEGAWATTS | ↓ → + W Citation Bolt · ↓ ↑ + W Bombs of Knowledge · ↓ ↑ + E Ground Truth |
| UNCLOSE | ↓ → + W Token Stream · ↓ ← + W Waveform · ↓ ← + E Free Tier |
Dojo · Night Market · Jungle Ruins · Mountain Airbase · Monsoon Palace · Storm Harbor
Each arena is a packed 240×160 RGBA scene with its own bounded procedural motif layer. The motifs animate independently at 7.5 Hz, while combat and input remain at 30 Hz.
Requirements: Node.js 22+, pnpm 9+, and ssh-keygen.
git clone https://github.com/thomasdavis/sshfighter.com.git
cd sshfighter
pnpm install --frozen-lockfile
pnpm run keygen
pnpm start
The server listens on 0.0.0.0:2223 by default. Configuration is entirely environment-based:
| Variable | Default | Purpose |
|---|---|---|
SF_HOST | 0.0.0.0 | SSH bind address |
SF_PORT | 2223 | SSH port |
SF_DB | data/streetfighter.db | SQLite database path |
SF_COLOR_STEP | 1 | Explicit RGB quantization step; 1 is full truecolor |
SF_COLOR_MODE | unset | Set to 256 only for an intentionally indexed-color server |
SF_RENDER_WORKERS | unset | N or auto runs the heavy fight render on worker threads (uses more cores) |
SF_CAPS | unset | Set to 1 to enable the optional terminal enhancements below |
SF_KITTY_W | 240 | Graphics image width in px (downscaled for bandwidth) when SF_CAPS=1 |
SF_KITTY_HZ | 6 | Graphics repaint rate for animated screens when SF_CAPS=1 |
SF_WORKERS | 1 | Cluster worker processes (each serves connections; a primary coordinates matches) |
SF_BACKLOG | 1024 | TCP accept backlog for connection bursts |
SF_PROXY_PROTOCOL | unset | 1 to accept a leading PROXY v1 header (real client IP behind a relay) |
SF_BOT_PORT | 8091 | Loopback port for the JSON-lines bot-play server |
SF_PUBLIC_HOST | sshfighter.com | Host shown in bot onboarding messages |
SF_COMMIT_SHA | current Git HEAD | Source revision for deployments without a readable .git checkout |
SF_BUILD_DIRTY | auto-detected | Optional true/false override for build cleanliness |
SF_DISCORD_WEBHOOK | unset | Optional best-effort Discord destination for vital community events only |
All events are recorded locally in the append-only analytics_events SQLite table for the planned analytics site. Discord receives only quick-match waiting, match start/result, forfeit, and lounge chat events. Inputs, special moves, screen views, connections, and terminal resolution changes never go to Discord. See the analytics contract.
Copy .env.example as a starting point. Never commit a webhook, host key, database, or gallery admin token.
For modern terminals (Ghostty, Kitty, WezTerm, VS Code, and others), SF_CAPS=1 turns on an experimental capability layer, negotiated per connection so anything unsupported degrades silently:
V to cycle quadrant → octant → graphics. Tune bandwidth with SF_KITTY_W / SF_KITTY_HZ.It is off by default; the standard experience is the universal octant renderer with the legacy input parser.
The optional Next.js site renders every packed pose as a PNG and publishes one responsive guide at /fighters/<name> for every roster entry. Profiles include original background stories, animated move sequences, tactics, inputs, damage, chip, range, and frame timing. A prebuild exporter reads the real roster, move, and engine modules before every web build, so the site cannot drift into a second hand-maintained combat database.
cd web
pnpm install --frozen-lockfile
pnpm build
pnpm start
It runs on port 3130. Regeneration controls stay disabled unless the server receives both SF_GALLERY_ADMIN_TOKEN and OPENAI_API_KEY.
The renderer preserves color and detail while limiting transport work:
900×360 terminal safety cap38;2 / 48;2) unless the operator explicitly opts into indexed modeSee the architecture guide for the render and session pipelines.
pnpm typecheck
pnpm test # combat, assets, persistence, ANSI reconstruction
pnpm test:e2e # lounge, challenge, matchmaking, and practice over real SSH
pnpm exec tsx src/dump-png.ts fight 112 36
The canonical engine compatibility family lives in src/version.ts. Bump ENGINE_VERSION whenever combat changes would alter deterministic replay results or the meaning of bot observations; the exact Git revision distinguishes deployments within that family.
The asset contract verifies all eighteen complete dossiers, all 54 explained special-move definitions, every required fighter pose, and all six stage payloads. CI also rebuilds the authoritative fighter catalog and the Next.js site.
src/
game/ deterministic 30 Hz combat, moves, roster, stages, sprites
render/ RGB pixel grids, octant/kitty render backends, exact ANSI diffs
screens/ menu, select, lounge, fight HUD, help, results, leaderboard
net/ SSH authentication, sessions, the Terminal I/O boundary, matchmaking, challenges
input/ key bindings + the fight input parser (legacy + optional kitty keyboard)
cluster/ optional multi-worker match coordinator
db/ additive SQLite schema, players, controls, ELO, match/chat/event history
telemetry/ local analytics plus vital-only non-blocking Discord delivery
assets/
sprites/ packed RGBA pose JSON files plus one generation anchor per fighter
stages/ six packed arena backgrounds
web/ Next.js fighter dossiers, animated sprite gallery, and guarded regeneration UI
Bug fixes, renderer work, accessibility improvements, terminal compatibility reports, fighter balance, sprites, and new arenas are welcome. Start with CONTRIBUTING.md, and please follow the community guidelines.
The public roadmap tracks privacy-safe aggregate analytics, additional fighters, and other contribution-sized milestones.
SSH Fighter is created and maintained by Thomas Davis (@ajaxdavis). More projects are at ajaxdavis.dev.
Code and original project assets are released under the MIT License. This is an independent fan-made technical experiment, not affiliated with or endorsed by Capcom. “Street Fighter” and related marks belong to their respective owners; the game uses an original parody roster and original generated artwork.
Hacker News (3)
TypeScript
89.6%
CSS
9.6%