Pocket Frogs resource — 45,264 combinations, weekly sets, and frog builder.
Game data mirrors 3.20.1. For pulling content out of a newer release, see UPDATING.md.
- Layer order matters.
renderFrogalways draws the grayscalefrog_base_256.png, then the genus mask, thenoverlay_256.png. Each layer is retinted by drawing it into a tiny canvas, adjusting the pixel colors (base uses theCOLORStable, genus usesPATTERN_COLORS) and painting that result into the preview canvas. - Overlay blending adds depth.
_multiplyOverlayimplements a manual multiply-style blend that mixes the overlay sprite’s luminance with whatever is underneath, so the frog keeps the shadows/shininess from the art. When the chosen color isGlass(color index 22) the routine also applies_applyGlassEffect, which cuts the alpha values by half to reproduce transparency. - Glitch-free fallback. Sprite loading is asynchronous, but a placeholder ellipse is drawn whenever
loadSpriterejects. That keeps the UI from freezing when files aren’t ready or the browser balks at the sprite cache.
Four genera have an extra sprite layer (frog_<genusId>_extra_256.png) that is composited on top of the standard layers. This layer is never tinted — it renders at its original colors regardless of the frog’s body color or pattern.
The layer order differs by genus because that is how the game itself composites them:
| Genus | ID | Layer order |
|---|---|---|
| Porto | 115 | base → overlay → genus → extra |
| Florens | 116 | base → genus → extra → overlay |
| Triquetra | 119 | base → genus → extra → overlay |
| Paschali | 120 | base → genus → extra → overlay |
Porto is the special case: its genus layer (the frog body) renders above the shading overlay, with the extra layer on top of that. All other third-layer genera follow the standard order with the extra layer slotted in before the overlay.
The set of genera that have a third layer is tracked in EXTRA_LAYER_GENERA (a Set in both index.html and api/og.js). Add a genus ID there and drop the corresponding frog_<id>_extra_256.png into frog_sprites/ to enable it.
Flagro (120 → genus 122) doesn't take a flat pattern colour at all. Its genus mask is filled with a tiled flame texture (frog_122_anim.png) that scrolls vertically, so the markings visibly flicker. _drawScrollingGenus builds one tile at cell / scale pixels, fills the mask with it at an animated offset, multiplies by the pattern colour, then keeps the mask's own shading with a multiply pass before clipping to its alpha with destination-in.
The config lives in ANIMATED_GENERA (again in both index.html and api/og.js) and mirrors animatedPatternTable.csv from the game. Animated frogs register in chromaCanvases alongside Chroma ones, so a single requestAnimationFrame loop drives both and the existing scroll-out cleanup applies unchanged — a Flagro frog with the Chroma pattern gets both effects at once. Server-side, api/og.js routes these through the GIF encoder so Discord embeds animate too.
loadSpritekeeps a promise cache so eachImageonly downloads once, and every sprite is fetched fromfrog_sprites/withcrossOrigin='anonymous'. That promise is reused anywhererenderFrogneeds the base, genus, or overlay textures, which was critical when dozens of species canvases try to draw simultaneously.
- Pattern 15 (
Chroma) is special: it doesn’t just tint the genus mask with a fixed color; it updates every frame.chromaCanvasesholds the canvas context plus the exposed RGB values and whether the current frog is glass, andanimateChromaruns onrequestAnimationFrameto sweepchromaHuethrough the HSV spectrum. That loop is also whyrenderFroghas to register the canvas in the map rather than drawing a single frame, the animation keeps running until the frog disappears from the DOM.
- Species cards and weekly set tiles render lazily: each canvas starts with
_drawFrogPlaceholder, and anIntersectionObserverpaints the frog only when it scrolls into view (and clears it when it leaves). This keeps the browsers from decoding 120 canvas images upfront while the user is still scanning filters. - Sets data is fetched from two sources (local
sets.txtfirst, thenhttps://www.nimblebit.com/sets.txtif needed), and the status element shows a friendly error when both fail. That double-fetch strategy came from noticing the GitHub Action snapshot already ships with a cached copy.
Hopefully these notes keep the rendering behavior transparent for anyone working in this folder. If you need a deeper dive, the relevant helpers live around the renderFrog, _tintLayer, and animateChroma functions inside index.html.
