Modes
tibo.dog has a Story mode and four levels (difficulties). The level picker captions come from the game's UI text.
Standard runs enter Hunt, Burrow, Court and Shell after 20:00; Story does not. Their family encounters can override ordinary enemy and boss stats.
| Mode | In-game caption |
|---|---|
| Story | “A short first game. Play this first.” |
| Easy | “Fewer, weaker pests and a tougher Tibo.” |
| Normal | “The game as designed.” |
| Hard | “More pests, harder hits, tougher bosses.” |
| Nightmare | “Crowds, a curse and no revives.” |
Difficulty multipliers
Verified values from the difficulty table in sim-BkfBhQfw.js. Each level starts from the defaults and overrides some of them; the Normal column shows Normal's own values.
| Setting | Easy | Normal | Hard | Nightmare |
|---|---|---|---|---|
Enemy count (alive) |
0.55 | 0.7 | 0.75 | 1.05 |
| Pest HP × | 0.5 | 0.65 | 0.65 | 1.2 |
| Enemy damage × | 0.4 | 0.55 | 0.6 | 0.95 |
| Boss HP × | 0.5 | 0.65 | 0.7 | 0.95 |
| Elite HP × | 0.5 | 0.65 | 0.65 | 1.3 |
| Tibo max HP × | 2 | 1.5 | 1.5 | 1.2 |
| XP × | 1.5 | 1.3 | 1.3 | 1.5 |
| Healing drop chance × | 1 | 1 | 1 | 0.8 |
| Alternate bullet patterns | No | No | No | Yes |
| Bullet orbit radius × | 1 | 1 | 1 | 1.2 |
| Bullet orbit frequency × | 0.6 | 0.8 | 0.6 | 1.1 |
| Shot-puddle radius × | 1.3 | 1.45 | 1.5 | 1.6 |
| Shot-puddle lifetime × | 1.5 | 1.65 | 1.65 | 1.65 |
| Returning bullet speed × | 0.7 | 1.05 | 1.05 | 1.05 |
| Alternate boss ground attacks | No | No | No | Yes |
| Boss ground-layout size × | 1 | 1 | 1 | 1.45 |
| Late damage ramp × | 1 | 1 | 1 | 0.32 |
| Late damage start time × | 1 | 1 | 1 | 0.333 |
| Added revives | 2 | 1 | 0 | 0 |
| Revives allowed | Yes | Yes | Yes | No |
| Starting curses | 0 | 0 | 0 | 1 |
| Enemy tier lag | 3 | 2 | 2 | 2 |
| Wall pests × | 0.15 | 0.3 | 0.3 | 0.6 |
| Overtime hold (s) | 60 | 0 | 0 | 0 |
| Crowd floor share | 0 | 0 | 0.25 | 0.65 |
| Opening | main | main | main | classic |
Verified: bullet orbit multipliers apply to Spittlebug froth pairs. Radius scales the circle around the pair's moving centre; frequency scales turns per second, not forward flight speed or burst timing. Story keeps ×1 for both.[1]
Verified: shot-puddle multipliers apply when a shot turns into a ground hazard, currently Ketchup Kraken's blobs. They multiply the puddle's base radius and lifetime, not its damage or the size of all ground attacks. Story uses ×1 for both.[2][3]
Verified: returning bullet speed applies only when a bullet's interpolated speed becomes negative, not while it flies outward. Sir Strawman's sickles use this reversal: Easy multiplies the return by 0.7; Normal, Hard and Nightmare by 1.05. Story keeps the default of 1.[4][5]
Nightmare rules
Verified: Nightmare's late damage ramp starts at 5:00 instead of 15:00 and adds 0.064 per minute instead of 0.2; its early damage ramp reaches full strength at 405 s instead of 900 s. These factors multiply the rest of enemy damage scaling; see Enemy scaling.[6]
Healing drops have ×0.8 chance: a normal pest's snack chance is 0.32% instead of 0.4%, and a Crop Patch's is 28% instead of 35%. The snack still heals 30 HP, and the other pickup chances do not change.
Nightmare uses alternate bullet patterns for amusing, Colorado Queen, The Fryer King, The Great Wireworm, Late Blight, Sir Strawman, The Masher, plus Blight Spore and Ice Imp. Other enemies and bosses keep their ordinary patterns.[7]
Verified: all 13 regular bosses have distinct Nightmare ground layouts: amusing, Colorado Queen, Compost Titan, The Deep Freezer, The Fryer King, Grater Golem, The Great Wireworm, The Harvester, Ketchup Kraken, Late Blight, Sir Strawman, The Mandoline, The Masher. Shapes include a heart, stripes, rings, a crop circle, a cross, wedges, a serpent, a checkerboard, a star, triangles, a spiral, a gapped ring and a wheel. Charges and summons can still happen alongside the layout; see each boss page for distances, damage shares and timing. Layout distances are ×1.45, not all enemy attacks. This does not give the later endgame bosses new layouts.[8][9]
Verified: enemy shot-path markers are shown on Easy, Normal and Hard, but hidden on Nightmare. This hides the path preview, not the shots themselves. The ground layouts still have their own warning shapes.[10][11]
Hard and Nightmare also use their own early crowd ramp (crowdRamp):
| Level | Alive enemies cap over the opening |
|---|---|
| Hard | 25 at 0 s, 35 at 80 s, 150 at 230 s |
| Nightmare | 10 at 0 s, 25 at 60 s, 70 at 130 s, 150 at 200 s |
Late crowds
Verified: Hard's ordinary spawn limit rises to 650, and Nightmare's to 800. A late spawn-ceiling floor ramps linearly from 0 at 16:00 to that limit at 17:00, taking the higher of that floor and the ordinary wave/curse ceiling. This raises the allowed crowd; it does not instantly spawn that many pests. Easy and Normal retain their wave-based limits and the overall maximum of 500. The extra hard-cap reserve is 50: 700 on Hard and 850 on Nightmare, versus 550 on Easy/Normal.[12][13][14]
Measured with each build's original spawning system, seed wiki, Standard, no combat, curse forced to zero and repeated top-ups at a fixed clock: Hard's cap at 16:30 rose from 285 to 325, then became 650 at 17:00 instead of 315; Nightmare rose from 399 to 400 at 16:30, then to 800 instead of 441 at 17:00. These are forced cap probes, not naturally played late runs.
Design targets
The difficulty table includes promises, which read as the developer's balance targets (Inference: the field name and values suggest intended outcomes, not mechanics):
| Level | Promise data |
|---|---|
| easy | {"kind":"mixReach","min":0.65,"max":0.75} |
| normal | {"kind":"mixReach","min":0.4,"max":0.5} |
| hard | {"kind":"mixReach","min":0.25,"max":0.35}{"kind":"lateCrowd","skills":["practiced","expert"],"minutes":{"first":18,"last":19},"minAlive":15,"min":650,"max":700} |
| nightmare | {"kind":"mixReach","min":0.03,"max":0.15}{"kind":"lateCrowd","skills":["practiced","expert"],"minutes":{"first":18,"last":19},"minAlive":15,"min":800,"max":850} |
All four levels use mixReach: Easy 65–75%, Normal 40–50%, Hard 25–35%, Nightmare 3–15%. The name suggests a target share reaching the goal across a mix of players, but the player mix and exact meaning are not confirmed by the developer. These are not measured survival rates.
Story
“A short first game. Play this first.” See Story mode. Verified: Story stays on the title screen after its ending, so you can replay it from there; only the end-screen pickers drop it. See Replaying Story.
Level picker
All levels are open from the start. After the Story ending, Easy is pre-selected and Story leaves the end-screen pickers, but it stays on the title screen (see Replaying Story). See Unlocks.
Level links
Verified: a link with a difficulty parameter, for example https://tibo.dog/?difficulty=hard, starts a run on that level straight from the title screen, without the intro. It accepts easy, normal, hard and nightmare; story and other values are not accepted, and shared run links ignore it.[15]
Shared runs
Finished runs can be shared as links under https://tibo.dog/r/ (the game's llms.txt). The developer says the deterministic simulation lets the server replay a posted run to check a high score, and that each run gets a generated preview image (trivia thread).
Verified: a shared run link records the run's difficulty, and the shared run page shows it as a level badge.[16]
Verified: the shared-run reader withholds a reconstructed score when kills reach the link format's 10,000,000 cap, peak enemies exceed 850, or a weapon/item damage total reaches its format cap in a run shorter than 90 minutes. This is a client-side score-display check, not proof of server validation.[17]
High-score replay submissions
Verified client code: with analytics consent and an ended-run ID plus a session ID, the game can POST the shared run, stats and encoded input log to /replay. A run qualifies when its reconstructed score reaches the level's threshold: 124,904 Easy; 74,546 Normal; 88,143 Hard; 137,597 Nightmare. Runs that fail the format-cap checks above also qualify, even if a score cannot be reconstructed; a missing difficulty does not.[18][19][20]
The request omits credentials and referrer information. The client accepts match, mismatch or known skipped replies; invalid replies and network failures become skipped/unavailable. This verifies the browser's submission path, not the server's replay implementation or a public leaderboard.[21]
Number readouts
Boss HP uses a shorter compact label: 8,432 HP displays as 8.4K, rather than 8.43K. Totals keep three significant digits, such as 8.43K, with suffixes beyond T for very large values. Kill counts and HP lost/healed on the stats screen shorten only when the full number does not fit the row; damage totals use the compact format directly.[22][23][24] Damage shares are calculated with a scaled sum, so extreme finite damage totals do not overflow into zero-percent rows.[25] These are display changes, not changes to damage or boss HP.
Long-run scores and numbers
Verified: new Standard runs use score version 3. Late survival points grow by 50% per minute after 20:00 until 60:00; after that the per-second rate stays fixed, so further survival adds points linearly. Dodging points use the same capped rate. Final scores cap at 9,007,199,254,740,991. Old shared runs retain their original score version.[26][27]
In a same-input score probe (123,456 damage dealt, 100 taken, 500 steps, 567 dodging points, 7 bosses, 30 held-curse percent, Normal), the old and new totals were both 27,977 at 20:00 and 42,083,145,740 at 60:00. At 120:00 the old capped total was 10,000,000,000,000; the new total was 847,695,941,060. These are formula probes, not naturally played two-hour runs.
Large damage and HP readouts use compact numbers such as 1.2K and 3.4M, with about three significant digits and further suffixes beyond T. Limit-broken equipment shows break marks in HUD slots, cards and shared-run stats.[28][29] ``]
Making a shared link
Verified: every finished level run gets a link of its own. When the stats screen opens, the address bar changes to https://tibo.dog/r/?run=<id>&stats=<data>.[30][31] The stats part packs the run summary: time, kills, level, gold, what reset Tibo, bosses, chests, steps, top damage sources, passives, items, biomes, difficulty and score parts. The run id is a 13-character hash of that data. A link whose id does not match its data is rejected.[32][33] The link is kept to 2,000 characters and does not carry the input log.[34]
This includes runs ended with Reset in the pause menu. Their stats screen and link name the killer as “Tibo's skill issue”.[35] Story runs never open the stats screen, so they get no link.
Share on X (key S) opens an X post with the text “Tibo got reset at 1:44 in @poteto's game. Think you can do better?” (“Tibo survived …” for a run that passed 20:00), followed by the link.[36][37] In some contexts the game shows the post text with a Copy post button and the card image instead; this wiki has not identified when.[38]
Played in the browser on the live game (build main-BnCKpQy_.js, 2026-11-05): a Nightmare run ended from the pause menu at 1:44 changed the address to a /r/?run=…&stats=… link, and Share on X opened x.com/intent/post with the text above and that link.
What a shared link shows
Verified: a /r/ link opens straight to the run's stats card: the title (“Tibo was reset after 1:44”), a level badge, the label “Shared run” and a Play button. Below them are score and its breakdown, kills, level, gold, what reset Tibo, damage by weapon, dog and item, passives, bosses, chests, distance, health, dashes and biomes.[39][40] Play removes the link from the address and goes to the title screen, with the viewer's own picker.[41]
The page needs nothing but the link: it rendered the same card in a browser profile with empty storage (live game, 2026-11-05). The server also builds a preview for each link. The page's Open Graph and X tags carry a title such as “Nightmare: Tibo's skill issue reset Tibo at 1:44.”, a description such as “369 kills, level 6, 202 gold, 0 bosses beaten.”, and a 1200×630 PNG card from /og/wide/<run id>.png. The X player card uses /og/square/ (fetched from tibo.dog, 2026-11-05).[42][43]
Leaderboards
There is no leaderboard in the game or on tibo.dog as of 2026-11-05. Players compare scores by sharing run links.
- Verified: the documented build has no leaderboard, ranking or high-score screen. No chunk of the
main-CSuyb4iv.jsbuild contains “leaderboard”, “ranking” or “high score”. The only score request is the POST to/replaydescribed above.[44] - Verified: the client can pass a replay verdict to listeners, but nothing in the build listens, so the player never sees a verdict.[45]
- Checked on tibo.dog (2026-11-05): the sitemap lists only
/,/about/and/privacy./leaderboard,/leaderboards,/scoresand/topreturn 404, and/replayanswers a GET with 405 (it only takes POST). - The privacy page says: “When Analytics is on and a run scores in the top 1% of its level, or its stats look impossible, the game also sends that run's inputs and card picks, so the server can check the score by replaying the run. The check keeps only its result.”
Inference: the replay check guards scores against faking (the changelog says “Top-scoring runs are now checked by replaying them, so faked scores don't count”[46]), but nothing public shows what they count toward.
Sources
Game version 75: main-CSuyb4iv.js / boot-CPW8VVmR.js / sim-BkfBhQfw.js from https://tibo.dog/, researched 2026-11-05. See Versions & updates.
- Difficulty table in
sim-BkfBhQfw.js - UI captions in
boot-CPW8VVmR.js
Code references
- sim-BkfBhQfw.js
let i=e.px*r.orbitPx,a=M*e.hz*r.orbitHz - sim-BkfBhQfw.js
i.radius[a]=t.radius*o.shotPuddleRadius - sim-BkfBhQfw.js
s=t.sec*o.shotPuddleSec - sim-BkfBhQfw.js
s=t.launch[n]*(o<0?o*mp.back:o) - sim-BkfBhQfw.js
gp(r,i,e.lastDashTick<0?pp:e.tick-e.lastDashTick,e.cfg.level.returnSpeed) - sim-BkfBhQfw.js
lateDamageFromMin:t.scaling.lateDamageFromMin*n.lateRampTime,lateDamagePerMin:t.scaling.lateDamagePerMin*n.lateRamp - sim-BkfBhQfw.js
return e.level.sharpPatterns&&n!==void 0?n:t.patterns - sim-BkfBhQfw.js
return e.level.sharpAttacks&&n!==void 0?n:t.attack - run-extras-l2haGSVw.js
j.scale=e.cfg.level.areaScale - boot-CPW8VVmR.js
telegraph:{pathMarks:{easy:!0,normal:!0,hard:!0,nightmare:!1}} - scene-B2rwbRKj.js
if(e.count=0,o===void 0||!X_e[t.difficulty])return 0; - sim-BkfBhQfw.js
crowd:650/wt.spawns.maxEnemies - sim-BkfBhQfw.js
crowd:800/wt.spawns.maxEnemies - sim-BkfBhQfw.js
hardCap:e.hardCap+n-e.maxEnemies,lateFloor:[{atSec:e.capRampFromSec,alive:0},{atSec:e.capRampFullSec,alive:n}] - boot-CPW8VVmR.js
e.runFrom=t.relaunch?`press`:`param` - run-address-C5YZEppN.js
tally:e.tally,difficulty:e.difficulty - run-score-D0NGJxyJ.js
e.tally.peakEnemies>Math.max( - boot-CPW8VVmR.js
topScore:{easy:124904,normal:74546,hard:88143,nightmare:137597} - run-replay-HUjZdu5_.js
if(!this.deps.consent() - run-replay-HUjZdu5_.js
n.total>=t[e.difficulty] - run-replay-HUjZdu5_.js
cache:`no-store`,credentials:`omit`,referrerPolicy:`no-referrer` - boot-CPW8VVmR.js
shortDigits:{bossHp:2,totals:3} - stats-panel-C2OXNxP3.js
let t=e.filter(e=>e.boxes.some(j));for(let e of t)e.shown.textContent=e.short - stats-panel-C2OXNxP3.js
function A(e){return b(Math.round(e),c.shortDigits.totals)} - stats-score-labels-D5k2nf1_.js
e/t.top/t.sumOverTop - sim-BkfBhQfw.js
xm=2**53-1,Sm=1+Bt.lateGrowthPct/100,Cm=ve(Sm,40) - sim-BkfBhQfw.js
Bt.pointsPerSecond*ve(Sm,Math.min(40,Math.ceil(t/60))) - boot-CPW8VVmR.js
maximumSignificantDigits:3 - short-parts.hot-C8LIaakH.js
e=[,K,M,B,T,Qa,Qi,Sx,Sp,Oc,No,Dc - run-address-C5YZEppN.js
show(){if(this.left)return;let t=e()?{}:{path:`/r/`} - run-address-C5YZEppN.js
function Z(e,t){let n=new URL(`/r/`,e) - limit-break-counts-BXP4C7z8.js
function J(e){return G(C(e))} - limit-break-counts-BXP4C7z8.js
if(J(r)!==e)return{kind:`invalid`,fault:`id-mismatch`} - boot-CPW8VVmR.js
runLink:{maxChars:2e3,deviceDetails:!1,inputLog:!1} - stats-panel-C2OXNxP3.js
message:[[`TIBO`],`'s skill issue`] - death-flow-BzA56B-v.js
Think you can do better? - boot-CPW8VVmR.js
share:{intentUrl:`https://x.com/intent/post`,hashtags:[]} - death-flow-BzA56B-v.js
Long-press the post text to copy it, then paste it into a post or reply on X. - shared-run-CmUzHiGu.js
testId:`shared-level` - shared-run-CmUzHiGu.js
message:[`Shared run`] - shared-run-CmUzHiGu.js
label:a._({id:`T91vKp`,message:[`Play`]}),onClick:i.play - death-flow-BzA56B-v.js
let t=`/og/${e.shape}/${e.run}.png` - death-flow-BzA56B-v.js
var U={width:1200,height:630} - boot-CPW8VVmR.js
replay:{path:`/replay` - run-replay-HUjZdu5_.js
onReplayVerdict(e){return this.listeners.add(e) - changelog-panel-DtW-mCDY.js
Top-scoring runs are now checked by replaying them, so faked scores don't count.