Home › JWorld Blog

180 Fish and No Artist: How Bobber Rise Draws Its Fish From Parts in Code

JWorld team ·

A dev note on how the JWorld team generates and draws all 180 album fish in Bobber Rise from a handful of code parts, with the real numbers, the distinctness rules and the tests that guard them.

Bobber Rise has an album of 180 fish. Every one has its own look, its own name, a size range and a pull pattern for the reel fight. Drawing 180 fish by hand would have taken a long time, and the game ships with a bundle target of 2.5 MB or less. So the JWorld team did something different: there are no fish image files at all. Every fish is drawn by code from a short list of parts.

This note walks through how it works, using the real source of the game. It covers the parts, the generator, the rule that keeps two fish from looking alike, and how drawing stays cheap.

The Bobber Rise album: 24 of 180 fish found. Each caught fish has a different body, tail, pattern and color, a size in cm and up to three stars. Locked cells show a grey silhouette and ???.
The Bobber Rise album: 24 of 180 fish found. Each caught fish has a different body, tail, pattern and color, a size in cm and up to three stars. Locked cells show a grey silhouette and ???.

The parts box

A fish is not a picture. It is a small record of choices, and the drawing code reads that record. These are the parts the drawer knows:

PartVariantsWhat it changes
Body12Silhouette: slim, round, deep, flat, torpedo, eel, boxy, spiky-back, big-head, long-snout, crescent, plump
Tail5Fan, fork, crescent, ribbon, round
Fins4 setsSmall, sail, long, double
Pattern8Plain, stripes, spots, bands, scales, belly gradient, saddle, speckle
Palette16Three colors each: body, pattern and accent
Accessory10Only on legends: crown, whiskers, glow, shell, horns, ribbon, lantern, gold star, halo and more

A body is not a stored image either. It is a formula. Each of the 12 bodies is a row of seven numbers (depth, how far along the peak sits, roundness, top and bottom multipliers, tail-end width and length scale). The code samples the outline at 28 points along the fish, walks along the top, back along the bottom, and closes the path. Changing one number turns a slim fish into a plump one. The same trick, on a smaller scale, draws the tails and fins.

On top of the shape come a few fixed touches that make all fish feel like one family: a single 2 px dark outline, one white highlight stripe on the back, and a big round eye. Because they are shared, even the strangest part combination still looks like a Bobber Rise fish.

The generator: a seed in, 180 recipes out

The recipes are not rolled while you play. A build-time script runs a seeded generator (the seed is the text fishing-v1) and writes species.json. The game only reads that file. That has two good effects. Every player sees the same album, and a fish never changes its look between updates.

Each species record holds its pond, tier, the part choices, a size range, name parts, a preferred time of day and zone, and its pull pattern. The count per pond and tier is fixed by design:

Tier (in game)Count
Common61
Uncommon52
Rare37
Epic20
Legend10

That makes 180 fish, 30 for each of the 6 ponds. Pond 1 has 14 commons, 10 uncommons and 6 rares and nothing above. Pond 6 has 6 commons, 7 uncommons, 6 rares, 6 epics and 5 legends. So the rarest fish live in the last waters.

The generator fills the most constrained tiers first (legends, then epics, rare, and so on). For each new fish it tries random part combinations, throws out any that break a rule, and keeps the best of up to 48 valid candidates. "Best" means two things. The fish should differ from its neighbours in the same pond in all three of body, pattern and hue. And it should use parts that are still under-used, so no body or color ends up everywhere. If a layout cannot be completed, the generator starts over with the next attempt number, up to 50 times.

What "different" means

The hard part of 180 fish is that random fish look alike. The generator enforces three rules.

RuleWhere it applies
Any two fish differ in at least 2 of 3: body, pattern, hue familyInside one pond (one album page of 30)
Any two fish differ in at least 2 of 4: body, pattern, hue family, tailAcross all 180
No two fish share the same body + pattern + paletteAcross all 180

The hue family is a group of 8 (pink-red, orange, amber, yellow, green, teal, blue, violet), with two of the 16 palettes in each. That keeps "two different greens" from counting as different fish.

We wanted the strongest rule, "2 of 3 for every pair among 180", at first. It turned out to be impossible. A code made of 12 bodies, 8 patterns and 8 hue families can hold at most 8 × 8 = 64 entries where every pair differs in at least two places. So the design document was changed: strict inside a pond, a looser four-part rule across the whole album. The rule is also honest about its limit. Two fish from different ponds can share a body and a pattern if their hue and tail differ.

There are tier rules on top, so rarity can be read at a glance:

  • Commons use only plain, stripes or spots.
  • Rares, epics and legends are never plain.
  • Epics always get the sail or long fin set.
  • Each of the 10 legends carries its own accessory. No two share one.

Names come from the same trick

Names are also built from parts: a prefix (24 options) and a noun (12 options, one per body). Prefixes are colors, skin patterns and places. English gives names like Jade Flatkin or Rosy Slimkin; the Korean names end in "-몽", such as 풀빛 통통몽. The generator picks the prefix that matches the fish's color or pattern first, and falls back to a place or mood word only if the pair is already taken. The noun matches the body, so a fish named "…Ribbonkin" is always the eel-shaped one. The names are fictional "pocket fish" and make no scientific claims.

A caught fish card in the English UI: Common tier, Morning Pond, "Jade Flatkin", 14.5 cm, one star, +4 coins, with the striped fish drawn large.
A caught fish card in the English UI: Common tier, Morning Pond, "Jade Flatkin", 14.5 cm, one star, +4 coins, with the striped fish drawn large.

Drawing: one function, any size

Every screen draws fish through a single function, drawFish. It takes the recipe, a position and a width in pixels, and fits the whole fish, tail included, into a box of that width and 0.75 of it in height. So a tiny album cell and the big catch card use the same code, and every species reads at the same size on screen. The size in centimeters changes only the numbers, not the picture.

A few details keep it light and clear:

  • Bounds are measured once. A recording stand-in for the canvas runs the drawing without painting and notes the extent of every point. The result is cached per body, tail, fins and accessory combination.
  • Small sizes get a simpler skin. Under 56 px, fine patterns like scales and speckle become a flat tint, because they would just turn to noise.
  • Dots are seeded by the species id. The spots on a spotted fish are placed by a tiny seeded generator keyed on the id, so the same fish has the same spots every time you open the album.
  • Locked cells are the same drawing. An undiscovered fish is drawn in silhouette mode: all fills and outlines at a faint dark alpha and no details. You can see its shape and guess what is waiting in the pond.
  • Extras are draw calls. A glow ring for legends, a rim for night water and a brighten effect for the burst warning are options on the same function.

Tests that keep 180 honest

Because the whole album is data, it can be checked by tests. The species tests in the repo do things like this:

  1. Regenerate everything from the seed and compare it with the saved species.json. If the generator or the file changes alone, the test fails.
  2. Check there are exactly 180 ids, and that each pond has the right count per tier.
  3. Compare every pair of fish, which is 16,110 pairs, against the distinctness rules.
  4. Check each tier rule, and that only legends carry an accessory, each a different one.
  5. Check every body, tail, fin set, pattern and palette is used at least once, and every size range stays inside its tier.

A pure-logic check also makes sure the core code never calls random or clock functions, so the same seed always gives the same result.

What this means for players

Behind the scenes it is a lot of arithmetic. On screen it is a simple promise: every cell in the album is a different fish, and rarer ones look more special. If you are filling the album, here is how to read it:

  • The silhouette of a locked cell is its real shape. Use it to guess which pond it belongs to.
  • Legends are the only fish with an accessory. A crown, a halo or a lantern means you have found one.
  • Each fish can earn up to three stars for size, so one catch is not always the end of that cell.

You can try the album yourself in Bobber Rise. For the reel fight and gear order, see our earlier guide to reeling.

Summary

  • Every fish is drawn by code from parts: 12 bodies, 5 tails, 4 fin sets, 8 patterns, 16 palettes and 10 legend accessories.
  • A seeded generator (fishing-v1) writes the 180 recipes at build time; nothing about a fish is random at play time.
  • Rules force fish to differ: 2 of 3 traits inside a pond, 2 of 4 across the album, and no repeated body + pattern + palette.
  • One strict rule for all 180 pairs is mathematically impossible with these parts (a 64-entry cap), so the final rule is split in two.
  • A single draw function handles every size, simplifies fine patterns on small cells and draws locked fish as silhouettes.
  • Tests check all 16,110 pairs, so a new fish can never quietly become a twin.

Written by the JWorld team from our game design documents and measured play data, with AI tools helping with the writing.

More posts

Gomoku Rules Explained: Free, No 3-3 and Renju

The same five-in-a-row game plays differently under different rules. Learn what each of the three rule sets forbids, how to spot 3-3, 4-4 and overlines, and how to practice with the daily puzzle.

guidegomokuboard games

Hwacha Fortress Beginner Guide: Blocks, Tiers, Boss Timings and Beacon Levels

How to survive your first ten minutes in Hwacha Fortress: steam charge and crushing, which blocks to pick, the four castle tiers, when the three bosses arrive, and what each beacon level changes.

guideHwacha Fortresssurvivor

Korea's Village Guardian Posts and the Five Elements: What Jangseung Guard Borrows and What We Invented

What jangseung posts were in Korean villages, how the five elements (wood, fire, earth, metal, water) work, and how Jangseung Guard turns them into game rules.

jangseungfive elementsKorean folklore

Harbor Haul guide: cargo types, wind, glass and barrels, and how to get 3 stars

To earn 3 stars in Harbor Haul you need to know what to place where and when to tap. The ten cargo types, crane timing, how to handle glass, barrels, wedges and wind, and the exact star conditions.

guideHarbor Haulphysics puzzle