catshit
v1.1.1
Published
Terminal arcade game — cats hit object, race the CPU to the catch position
Downloads
86
Maintainers
Readme
🐱 catshit
Pure terminal. Pure ASCII. No browser, no Electron, no cloud, no accounts.
██████╗ █████╗ ████████╗███████╗██╗ ██╗██╗████████╗
██╔════╝██╔══██╗╚══██╔══╝██╔════╝██║ ██║██║╚══██╔══╝
██║ ███████║ ██║ ███████╗███████║██║ ██║
██║ ██╔══██║ ██║ ╚════██║██╔══██║██║ ██║
╚██████╗██║ ██║ ██║ ███████║██║ ██║██║ ██║
╚═════╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝
Cats hit something. You race the CPU.
First to catsh it wins.
╭─╮
/\_/\ ─┴─┴─ /\_/\
(^.^= )~ (°-°) ( =^.^)>
UU-UU ´H` UU-U´
Install
npm install -g catshitRequires Node.js 18 or later.
Play
catshit🕹️ How to play
Aiming — lock 3 timing bars
Before each throw, three bars bounce back and forth. Hit the key to lock each one in sequence:
| Bar | What it controls | |-----|-----------------| | Strength | How hard the cats hit — affects distance | | Angle | Launch angle — affects height vs. distance trade-off | | Mass | Object weight — affects speed, drag, and bounciness |
| Key | Action |
|-----|--------|
| Space or Enter | Lock the current bar |
| Backspace | Cancel → back to settings |
In flight — race to the spot
Once the object launches, you and the CPU both race to stand under the landing position. The CPU samples the arc and predicts where it'll land. So do you — except you can see the whole arc.
| Key | Action |
|-----|--------|
| ← / A | Move left |
| → / D | Move right |
| Shift + ←/A | Sprint left |
| Shift + →/D | Sprint right |
| Space | Jump — catch mid-air at the apex |
| Enter | Commit catch at current position |
| Q / Ctrl+C | Quit |
You don't need to press Enter to catch — standing under the landing spot when it hits the ground counts too. But committing early (Enter) lets you score while the CPU is still running.
After each round
| Key | Action |
|-----|--------|
| Any key | Play again |
| M | Return to main menu |
📋 Game modes
Demo
Free play. Before each throw you can tune hit strength, launch angle, object, wind, and gravity. Nothing saves. Great for getting a feel for the physics.
Campaign
Seven handcrafted levels with increasing CPU speed and physics challenges. Each level has:
- A specific object (ball, feather, or rock)
- A fixed CPU CPM (calculations per minute — how fast the CPU predicts)
- Optional physics modifiers (altered gravity, max wind, drag)
- Up to three stretch goals that award bonus points and ⭐ stars
Win a level to unlock the next. Progress saves automatically to ~/.catshit.json.
💩 Objects
| Object | Mass | Drag | Bounciness | |--------|------|------|------------| | Default | 1.0 | 0 | 0 | | Ball | 1.0 | 0.02 | 0.3 — bounces away on a CPU win | | Feather | 0.2 | 0.08 | 0.1 — floats, drifts with wind | | Rock | 2.5 | 0.005 | 0.05 — fast and heavy |
Heavier objects travel farther and are less affected by drag. Objects with bounciness roll away after a CPU win, giving you a visual cue that you lost.
🏆 Scoring
Score = gap × time
| Component | What it measures | |-----------|-----------------| | Gap score | How far the CPU was from the landing spot when you committed | | Time score | How many seconds faster you were than the CPU would have arrived | | Goal bonuses | Flat point bonuses for hitting level objectives | | Hat bonuses | Wearable hats extend your mid-air catch range (Y-axis tolerance) |
Points accumulate across all rounds. Spend them in the Shop on consumables and wearable hats.
🧩 Mods
catshit is built mod-first. The official campaign, all objects, and all cosmetics ship as built-in mods. You can add your own.
Enabling mods
Main menu → Mods — toggle any installed mod on or off. Your selections are saved to ~/.catshit.json.
Installing a mod
Drop a folder containing a mod.json into the mods/ directory next to the catshit package install (e.g. node_modules/catshit/mods/my-mod/mod.json).
mod.json — full schema
{
"id": "my-mod",
"name": "My Mod",
"version": "1.0.0",
"core": false,
"content": {
"menuItems": [
{ "label": "My Campaign", "action": "CAMPAIGN" }
],
"objects": [
{
"id": "rubber-duck",
"name": "Rubber Duck",
"mass": 0.5,
"dragCoefficient": 0.04,
"bounciness": 0.6
}
],
"campaign": {
"id": "my-campaign",
"name": "My Campaign",
"levels": [
{
"id": "level-1",
"name": "First Throw",
"hitStrength": 14,
"cpuCpm": 70,
"objectId": "rubber-duck",
"modifiers": { "wind": 2, "gravity": 9.8 },
"physics": { "minLaunchAngle": 30, "maxLaunchAngle": 60 },
"goals": [
{ "type": "distance_gt", "value": 30, "label": "Land past col 30", "scoreBonus": 20 },
{ "type": "altitude_gt", "value": 10, "label": "Reach altitude 10", "scoreBonus": 15 },
{ "type": "catch_within_seconds", "value": 5, "label": "Catch within 5s", "scoreBonus": 25 }
]
}
]
},
"shopItems": [
{
"id": "power-throw",
"name": "Power Throw",
"description": "Boosts hit strength by 5 for one throw",
"price": 500,
"category": "consumable",
"effect": {
"type": "modify_config",
"params": { "minHitStrength": 17, "maxHitStrength": 27 }
}
}
],
"cosmetics": [
{
"id": "duck-hat",
"assetType": "hat",
"spriteAnchor": 2,
"poses": {
"idle": [">v<"],
"catch": [">v<"],
"miss": [">v<"]
}
},
{
"id": "duck-face",
"assetType": "face",
"poses": {
"idle": ["(o_o)"],
"catch": ["(°o°)"],
"miss": ["(;_;)"]
}
}
],
"cpuStrategies": [
{ "id": "my-strategy", "module": "strategy.js" }
]
}
}Extension points
| Type | What it adds |
|------|-------------|
| objects | New throwable objects with custom physics (mass, drag, bounciness) |
| campaign | Full campaign — levels, goals, physics modifiers, object overrides |
| menuItems | Extra entries injected into the main menu |
| shopItems | Purchasable consumables with modify_config effects applied at throw time |
| cosmetics | Hat, face, body, outfit, and full cat sprite replacements |
| cpuStrategies | Custom CPU AI — a JS module that receives the full arc and returns updated CPU state |
Goal types
| Type | Condition |
|------|-----------|
| distance_gt | Landing position > value (columns) |
| distance_lt | Landing position < value |
| altitude_gt | Peak height > value |
| altitude_lt | Peak height < value |
| hangtime_gte | Time airborne >= value (seconds) |
| catch_within_seconds | Player catches within N seconds of launch |
Each goal that is met adds scoreBonus points and counts as one ⭐ star.
Writing a CPU strategy
A strategy module exports a single default function. It receives the current CPU state and arc data, and returns a new CPU state:
// strategy.js
export default function myStrategy(cpu, arc, arcComplete, tick, arcFrame, wind) {
// cpu — { position, prediction, samples, arrived, ... }
// arc — [{ x, y }, ...] full trajectory (world coordinates)
// arcComplete — true when the full arc is visible
// tick — current game tick
// arcFrame — leading arc frame index (only frames up to here are "visible")
// wind — per-throw lateral force (positive = rightward)
// Move directly to the landing point once arc is complete
if (arcComplete) {
const landing = arc[arc.length - 1];
return { ...cpu, position: landing.x, arrived: true, prediction: landing };
}
return cpu; // do nothing until arc is complete
}🔮 What's next
- 📦 Community content — more objects, campaigns, cosmetics and CPU strategies via mods
- 🐾 More cat skins — full cat sprite replacements via the
catcosmetic type
The mod extension points are stable. Community mods are welcome.
Uninstall
npm uninstall -g catshitNo ads. No subscriptions. No online requirement. Just cats hitting, an object, and a race to the catch.
