PROCITY/docs/ANIMALS_ANSWER.md

28 KiB
Raw Blame History

FABLE'S ADJUDICATION (2026-07-26) — read before §1. This spike is ratified: the mechanism is proven on our own vendored bytes, and the tiering stands. One verdict in it is over-claimed and is corrected here. The spike calls the four module-scope furniture GLB fetches under ?classic=1 "a live covenant breach in shipped code". They are not a breach: bench/bin/bus_shelter/food_cart entered furniture.js in Lane B round 6 (24cef5a), which is tagged v2.0-alpha through v2.0 — git-verified. They are part of the v2 baseline the covenant preserves, so classic fetching them is classic being correct. "Zero fetch delta" has always meant zero delta against the documented v2 baseline, not zero requests.

What the spike really found, and it is valuable: (a) classic_regression carries no network assertion at all, so it could not detect a future fetch-surface widening — which is exactly the R36 species (five DJ bodies widened classic's fetch surface and every pinned number stayed green; only a NEW smoke caught it); and (b) furniture.js's module-init load pattern is a trap for new assets — anything added to GLB_FILES fetches under classic silently. Both corrections are binding on the fauna work: fauna.js is dynamically imported behind its construction guard or carries a CLASSIC-derived module-scope guard stated in its header, and classic_regression grows a network allow-list (the four v2 depot GLBs plus same-origin), never a deny-list. A deny-list only catches what you already thought of.

Also noted: the spike could not reproduce the 280/300 walked draw figure (it measures 191 default / 167 classic by teleport-walk). Re-pinning the budget of record is now wave 0's first job, and no proposal spends against 280 until a walked re-measure settles it.


ANIMALS — THE ANSWER

PROCITY fauna. Answering: "yeah idk about rigged animal models, i will see if i can find some to use online, they have different rigging too right? diff skeleton? what do you suggest here"


1. Short answer

Yes, every animal has a different skeleton — and it turns out not to matter. There is no mixamorig for quadrupeds. There is no standard animal rig at all. Every animal you download ships a bespoke skeleton with bone names its author invented that morning: leg_fl, Bone.003, Hind_L_01, whatever. That part of your instinct is exactly right.

What is wrong is the assumption underneath it — that a different skeleton is a problem. It's only a problem if you need to make one clip play on a different body. That's retargeting, and it's the reason our human stack exists: 24 human bodies share 8 clips, so every body must speak mixamorig. An animal that ships its own animations inside its own file doesn't need any of that. The GLB binds its clips to its own bones, through its own mixer, and plays. Zero retargeting.

We measured it rather than assumed it. There is no real animated non-mixamo GLB anywhere on the fleet — I scanned all of ~/Documents: 2,486 GLBs, 81 with animations, 81/81 mixamorig, 0 non-mixamo skins. So we authored a test asset byte by byte: a 10-bone quadruped named root/spine/neck/head/tail_01/tail_02/leg_fl/leg_fr/leg_bl/leg_br, 96 tris, two embedded clips named Blender-style (Armature|CritterWalk, Armature|CritterIdle). Then loaded it through our actual vendored three.js r175 bytes and our actual web/js/core/loaders.js, with the repo untouched, and rendered it on a real WebGLRenderer.

Result — 27 assertions, all green:

  • A bare new THREE.AnimationMixer(gltf.scene) + clipAction(gltf.animations[0]).play() — no canon, no buildFigure, no _rotOnly, no MIRPAMO — moved leg_fl by quaternion delta 0.252 then 0.310 across two mixer.update() calls.
  • The GPU agreed. Rendered to a 200×150 render target and read the pixels back: 1,804 lit pixels, and 7041,015 of them changed between frames. Not "the matrices moved" — actual different pixels came off the GPU.
  • SkeletonUtils.clone works on the foreign rig: separate skeletons, shared geometry and shared material, so one file feeds N animals. (It maps by object identity, not by name — SkeletonUtils.js:388-425 — so "foreign names are fine" is structural, not luck.)
  • Our own loadGLB() returned the identical object unmodified. No change to any existing module.

And the mirror image, which is the important half: if you fed that animal to web/js/citizens/rigs.js, _canon() would silently do nothing (no mixamorigN names to normalise), and then buildFigure() would measure the quadruped's "feet-to-crown" span at 0.36 m, normalise it to human height, and scale the dog 4.9×. No error. A giant dog.

So the law is: animals get a new sibling module, web/js/citizens/animal_rig.js, that imports nothing from the human rig stack. About forty lines. The human fleet is classic-covenanted and never gets touched.

The real constraint is draws, not skeletons. Measured on a real renderer: 1.00 draw call per skinned animal, 1:1, all the way to N=80. three.js has no instancing for animated skinned meshes and there is no impostor tier for animals (our impostor bake frames square from height — impostor.js:57-66 — which crops a long low animal). So a skinned animal is a full draw at every distance it's visible, or it's culled outright. Nothing in between.

Against that: sixty birds on wires cost one draw between them, because static instanced meshes share one material and one draw call. That asymmetry — 1 draw per dog vs 1 draw per sixty birds — is the entire design.


2. The recommendation

Four tiers. Only two of them ship this epoch.

TIER 0 — the heard animal · +0 draws, +0 triangles · SHIP

A cockatoo you hear at dusk and never see costs literally nothing and does most of the work. New entries in PACK (pipeline/gen_audio.py:688), ~30 lines of numpy each, deterministic, original, 🟢 by construction. Played at a seeded anchor, so it's the same dog behind the same fence every time you walk that street.

This is the highest charm-per-minute item in the entire document. A town with zero visible animals reads as a place with animals in it. Nothing can regress; it inherits the existing smoke_audio gate for free.

TIER 0a — the flock · +1 draw for the whole town · SHIP — this is wave 1

One InstancedMesh, one geometry (two crossed planes = 4 tris), one material, one 1024 WebP species atlas. Cockatoo, galah, corella, lorikeet, perched magpie, ibis are all cells in the same sheet — which means every future bird species is free. That single decision is the most valuable thing in the plan.

This is not a new trick: it's exactly what already renders the town's entire tree canopy (furniture.js:98-101). Matrices are static; the buffer refills only when you cross a 64 m chunk boundary. Zero per-frame CPU.

And it is genuinely free when empty — verified to the vendored line. three.module.js:2193-2199 returns from renderInstances before the GL call, and render.calls++ lives at three.module.js:4094 inside info.update(), downstream of that return. A town with no birds, or the daytime species at night, costs exactly zero, not "almost zero".

Live cap 96 instances → 384 triangles. Against ~76k spare triangles that is a rounding error.

TIER 0b — the near props · +1 draw per species · wave 1.5, one species

Real low-poly meshes for the ones you walk within two metres of, where a billboard reads as cardboard. The ibis, head down in a council bin — it's the named proof species from the last review and it lands next to the existing council-bin prop. One town-wide instanced mesh, ≤450 tris, cap 16.

TIER 1 — static mesh + scripted path · +1 draw · the magpie, later

The already-approved swooping magpie: position is a pure function of (seed, elapsed time) — the tram's exact contract — with the wing wave riding wind.js's existing amplitude uniform, which is byte-identical at amplitude 0.

One correction that's binding: the review caught that the magpie's territory has to scale with the road graph. On the synthetic golden town there are 370 pole anchors; on bendigo_real there are 10,014 poles and 13,730 tree anchors across 2,593 edges. A capacity-4 magpie town-wide on bendigo is a swooping town where you never meet the magpie. That was already ruled on once (REVIEW_CITY_PATTERNS.md:111) and this plan has to honour it, not quietly re-open it.

TIER 2 — genuinely skinned · +1 draw each, uninstanceable · CUT for this epoch

The mechanism works. We proved it. We are still not shipping it, and the argument that killed it came from our own plan.

The justification for a skinned animal was: a quadruped gait is the one motion every human eye detects as wrong before it can read, so we can't fake a walking dog. True. But the actual spec anchored the dog at shopFronta dog tied outside the shops, with a static tied dog as its ?noassets=1 fallback. A tied dog doesn't trot. The walk clip, the sole and entire reason the tier exists, would never play.

So either the dog walks — which needs a path, footpath alignment, a collision story and a sim hook that the design explicitly disclaims ("the ped sim never learns animals exist") — or the tier buys an idle animation for one uninstanceable draw each, which is exactly what tier 0b already delivers inside a draw it shares with fifteen others.

Cut. Along with the download that fed it. Revisit only with a walking dog spec that owns its path, its cull and its sim hook. Different round, different charter.

The ledger

tier mechanism draws ship?
0 heard numpy synth, seeded anchor 0 this week
0a flock 1 instanced mesh, 1 atlas, town-wide +1 wave 1
0b prop 1 instanced mesh, 1 species (ibis) +1 wave 1.5
1 scripted 1 instanced mesh (magpie) +1 later, graph-scaled
2 skinned uninstanceable, +1 each +1..2 CUT
the wire town-wide instanced spans +1 see §6

Wave-1 spend: +1 draw. The original plan said +7. The review cut it to +1 and the cut holds.

One thing to fix before any of this is charter law: the budget of record says the walked worst frame is 280/300 draws. Re-measuring headless I get 191 default / 167 classic by teleport-walk, and can't reproduce 280 — it's almost certainly a transient during the chunk-dispose window, which teleport sampling can't hit. It stands as the conservative number, but spending against a figure nobody can currently reproduce is the weakest joint in the whole thing. Re-pin it first. Fauna helps here: its layers are town-wide, not per-chunk, so they take no part in the dispose window at all — their cost is steady-state.


3. INTAKE CHECKLIST — one screen, for while you're browsing

ACCEPT ON SIGHT

  • CC0 / public domain. No argument, no attribution obligation. Still record it in SOURCES.md — our discipline, not their demand.
  • Single .glb, glTF 2.0, or a .gltf sitting next to .fbx (Quaternius ships both).
  • A named clip list you can actually read: "Walk", "Idle", "Run", "Gallop". Not the word "rigged" on its own.
  • Under 3,000 triangles. Under 900 for anything that'll be a bird.
  • One mesh, one material. Draws are the scarce resource.
  • Textures embedded in the file.

REJECT ON SIGHT

  • A "Buy" or "Add to cart" button. TurboSquid, CGTrader, Sketchfab Store, paid Fab. See the licence rules — paying does not fix this, and this is the most expensive avoidable mistake on the list.
  • "Rigged" with no clip list. A rig with no animations is a static mesh with extra bones, and we cannot animate a quadruped ourselves — MIRPAMO is biped-only by construction, Mixamo's auto-rigger is verified humanoid-only. Worth exactly zero to us. We can make a static mesh in three minutes for free.
  • Five- or six-figure triangle counts, and the photogrammetry/museum-scan look that produces them. The free "White Ibis" on Sketchfab is 238,200 triangles — one bird exceeding our entire 200k street budget — and it doesn't move. Research and heritage-scan accounts are a reject-on-sight category, not individual bad models.
  • Draco / KTX2 / meshopt compression (look for extensionsRequired). We vendor no decoders. Verified: the loader throws, our fail-soft wrapper (loaders.js:38-44) turns the throw into null, and the animal silently does not exist. A console.warn nobody reads. Downloaded assets in the wild are heavily Draco-compressed — this is the single most likely silent failure.
  • Right common name, wrong animal. The free "White Ibis" is Eudocimus albus, the American one — not our bin chicken (Threskiornis molucca). "Magpie" on stock sites is almost always the European bird, which looks nothing like ours. Australian fauna is badly served by international libraries, and a wrong-species bird reads as wrong to every Australian and is invisible to everyone else.
  • NC / ND / "free for personal use" / "editorial use only". Instant reject including when it costs nothing.
  • CC-BY-SA or GPL-on-art. Share-alike is viral, and we retexture and rescale everything — we'd always be making derivatives. Would compromise a provenance table that's been clean for seven epochs.
  • No licence stated anywhere — page, description, or the README in the zip. Default under copyright is all rights reserved. Silence is not permission. Close the tab; don't email and negotiate.
  • Real brands, sponsor decals, team logos, recognisable people. AUDIT.md already quarantined branded assets once.

🟢 THE LICENCE RULES

  • 🟢 GREEN — CC0. Accept freely. Record URL, author, licence string and download date in SOURCES.md anyway.
  • 🟡 AMBER — CC-BY. Usable commercially forever, but the attribution obligation is perpetual and travels with every build. Hard rule: the SOURCES.md line goes in the same commit as the file, never "later". CC-BY is the one licence class that becomes a breach through nothing worse than forgetfulness.
  • 🔴 RED — every paid marketplace royalty-free licence, structurally, at any price. This is the finding that saves money rather than spending it. TurboSquid states it outright: stock media "must be contained in proprietary formats so that they cannot be opened or imported in a publicly available software application or framework, or extracted without reverse engineering" — then names the only exceptions: WebGL exports from Unity, Unreal, Lumberyard and Stingray. PROCITY is hand-rolled three.js with no build step, serving raw .glb over plain HTTP. A player presses F12 and saves the file. That is the named prohibited case. Sketchfab Store forbids letting third parties "use, download, extract or access" the asset — same wall. CGTrader forbids passing it on "in the form it is downloaded from the site" — same wall, blurrier wording, which is its own reason to stay out. A $18 rigged possum is exactly as unusable as a free one.
  • "Free" is not a licence. It means "costs zero dollars" and says nothing about rights. Free3D's own terms cap "substantially similar creations" at personal use, max 5 instances.
  • Capture provenance at download time, not audit time. Epic is folding Sketchfab into Fab, and CC0, CC-BY-SA, CC-BY-NC and CC-BY-ND cannot migrate — those licences don't exist on Fab. Free downloads have been on borrowed time since 2025. itch.io packs go paid or vanish without notice. Archive the file in-repo and record the exact licence string the same day. A dead URL with no recorded licence is an asset we can no longer prove we may use.
  • Internal conflicts = walk away. If the itch.io page says CC0 and the README in the zip says "credit me", don't adjudicate and don't negotiate. Take the unambiguous asset instead. Ambiguity is what eventually contaminates the table.

4. VETTED SOURCES

source URL licence animated? verdict
Quaternius quaternius.com CC0 1.0 Yes, genuinely — "Ultimate Animated Animal Pack": 12 animals × 12+ named clips The only one that matters. glTF shipped alongside FBX so no conversion. Low-poly, single-material, flat-shaded — our exact budget shape. Roster is Cow/Donkey/Deer/Alpaca/Bull/Fox/Shiba/Stag/Husky/Wolf/Horse. Zero Australian natives, zero birds.
itch.io (Ariescar) itch.io/game-assets/free/tag-3d/tag-animals CC0 (per pack — read carefully) Yes "10 free CC0 rigged & animated low-poly animals (GLB) for Three.js" — literally built for our stack. Highest signal-to-noise after Quaternius. Licence sloppiness is the risk: page body and zip README often disagree.
Poly Pizza poly.pizza Mixed CC0 / CC-BY per model Mostly no Fast browse surface for Quaternius, not a source in its own right. The site most likely to sneak a CC-BY model into a folder of CC0 ones — read the badge on the individual model, never the bundle header.
Kenney kenney.nl/assets/animal-pack-redux CC0 No — animals are static Skip. Static is the one thing we already make locally for free. Excellent for props/UI/audio, just not this.
Sketchfab (CC filter) sketchfab.com — filter licenses=CC0 Per model, CC0→CC-BY-NC-ND all coexist; CC-BY is the default Sometimes Only big library where you can filter Animated + Downloadable + permissive at once. Two warnings: Store ≠ Sketchfab (any "Buy" button is forbidden to us), and downloads are on borrowed time due to the Fab migration.
OpenGameArt opengameart.org CC0/CC-BY/CC-BY-SA/OGA-BY/GPL Thin, dated The share-alike trap lives here. Filter to CC0 and CC-BY only.
Icosa Gallery icosa.gallery Mostly CC-BY No — the rescued Poly corpus is Tilt Brush/Blocks, completely motionless Cross it off. Anyone calling it "the Google Poly successor" hasn't checked whether the models move.
Blender Studio studio.blender.org/characters CC-BY / some CC0 Rigged, but .blend CloudRig control rigs, not baked GLB clips Wrong shape. Film-density rigs meant to be hand-animated. Quality benchmark, not a download.
TurboSquid Paid RF Yes, incl. a rigged brushtail possum 🔴 STRUCTURALLY FORBIDDEN. See licence rules. Do not spend money.
CGTrader Paid RF + free tier under RF terms Yes 🔴 Same wall, blurrier wording. A provenance table wants "clearly permitted", not "probably defensible".
Free3D Mixed and hostile Sometimes Per-asset licence archaeology on every file, real chance the answer is "no commercial" after you've downloaded. Not worth opening.
Meshy / Tripo Free tier = CC-BY Largely no Competitors to our own pipeline, not suppliers. We generate static meshes locally for $0 with provenance we control end to end. Trading that for a murkier third-party line gains nothing.
Mixamo Free w/ Adobe account Biped only — confirmed Auto-rigger requires a humanoid in T/A-pose and rejects quadrupeds. Adobe stopped developing it years ago. No path here.
Anything World anything-world.gitbook.io SaaS, ~$50/mo Yes, incl. animals Technically the closest thing to a magic wand, still a no: cloud dependency on a startup, and $50/mo for what turns out to be a one-dog problem we've now cut.
Blender Rigify docs.blender.org GPL (the tool; your rigs are yours) Ships quadruped, cat, wolf/dog, horse and bird metarigs Worth knowing, and it corrects our own record. We wrote down "we cannot locally rig a non-humanoid". True of MIRPAMO; not true of the fleet — Blender 5.0.1 is already on ultra. The gap is animator hours, not tooling. That's a scheduling fact, not an architectural wall, and the door stays open if you ever want an afternoon in Blender.

5. SPECIES SHORTLIST

species tier download? why
Sulphur-crested cockatoo 0a flock No — generate Screaming on a wire at dusk. All the charm is silhouette, placement and noise. Perched = zero locomotion. Five cockatoos for one draw, which no rigged model can ever match.
Galah 0a flock No Same mesh as the cocky, second skin. flux_local does a skin in ~5.7 s — the second bird is nearly free.
Corella 0a flock No Third cell on the same sheet. Free.
Rainbow lorikeet 0a flock No A screeching mob in a flowering gum. Instanced statics.
Australian white ibis 0b prop No The joke is entirely postural — head in the bin, or standing on one leg looking personally aggrieved. Nobody has ever laughed at an ibis's walk cycle. The named proof species; keep it in wave 1.5. Note: nothing usable exists to download — the free one is the wrong continent at 238k tris, and the correct 4.2k-tri one is a Store listing.
Magpie (swooping) 1 scripted No Already approved. Static mesh on a seeded parabola, wing wave via the existing wind uniform. Being swooped in September is shared national trauma. Must scale territory with the road graph.
Brushtail possum 1 scripted No Frozen on a fence rail at night with eyeshine — which is what a real possum actually does. The cheapest implementation is also the most accurate. Only proper rigged one found is $18 TurboSquid, forbidden at any price.
Brush turkey 0b later No Bonus pick, and a strong one — the canonical town is redhill_godverse, Red Hill, Brisbane. Genuine local menace. Signature behaviour is standing next to a mound scratching, not walking. Too regional to exist in any stock library anyway.
Blue-tongue / cane toad / flying fox 0a/1 later No Long tail, all near-zero cost. Toad = scripted hop. Flying fox = the magpie's parabola with different geometry, reused for free.
Dog (kelpie/heeler) 2 Quaternius Shiba CUT. The only species that ever earned a download, and it was refuted by its own spec — anchored tied outside the shops, so the walk clip never plays. Also: a Shiba's curled tail and prick ears are geometry, not textureflux_local retexturing gives you a tan Shiba, not a kelpie. The dog survives at tier 0 as a bark behind a fence, which is free and, honestly, more 1990s Australian than a visible dog.
Gull, pigeon, cat, flies, crow CUT. Nothing about any of them says Australia, let alone 1990s.

And the thing that isn't an animal: the single most 1990s-Australian-country-town object on this whole list is the overhead powerline. A main street with streetlights and no wires reads as a 2010s estate. The wire is worth more period-load per draw than any two bird species on the sheet — and it's the thing the birds are for. Wave 1 perches on the streetlight arm (furniture.js:77-80, y≈4.1) because it's free and period-plausible; a real wire is +1 draw, town-wide instanced, and needs the pole-pairing worked out first (poles alternate sides every 18 m, so same-side spans are 36 m and must not cross edge boundaries or you get wires cutting through intersections).


6. Two things that block, independent of animals

1. ?classic=1 is already fetching four off-origin GLBs. I verified the mechanism directly: index.html:52 statically imports chunks.js, which imports furniture.js, whose module body fires loadGLB() at line 63-67 guarded by NOASSETS only — never by CLASSIC (index.html:94). ES module bodies run on import; construction guards are irrelevant to them. Measured: 179 requests, 25 .glb, 4 off-origin depot fetches (bench, food cart, bin, bus shelter) under ?classic=1. And classic_regression (flags_check.py:128-241) has no network assertion at all, so it goes green.

This is a live covenant breach in shipped code, and it matters here because that module-init pattern is precisely what the fauna plan proposed to copy. Binding: fauna.js is either dynamically imported behind its construction guard or carries a CLASSIC-derived module-scope guard, stated in the file header — and classic_regression grows a network allow-list (the four known depot GLBs plus same-origin), not a fauna-shaped deny-list. A deny-list only catches what you already thought of, which is exactly how this got through.

2. normalize.py silently eats rigs. Line 95 deletes every non-MESH object (its own comment says "empties, armatures, cameras, lights"), line 101 joins, line 105 clears armature modifiers. Point the existing pipeline at a rigged animal and you get a T-posed statue with the clips gone and a green run. Same for decimate_props.py:37-45 and bake_lowpoly.py. Write it into their docstrings and make the intake tool refuse to run them.

Worth landing regardless of whether an animal ever ships: the spike's deliberate-failure fixtures (bad_draco, bad_cm, bad_zup, bad_rootmotion, bad_2mat, bad_exttex) move into pipeline/_fixtures/ and become a permanent regression suite for free.


7. NEXT ACTION

What Fable can start unattended this week, $0, no download, nothing to approve

Tonight — the audio batch. Six numpy synth functions into gen_audio.py's PACK: magpie clack and carol, cockatoo screech, dog bark (fence and near), ibis grunt, crow. ~30 lines each, deterministic, 🟢 original, zero draws and zero triangles. This is the highest charm-per-minute item in the entire design and it cannot regress anything. A town with no visible animals starts reading as a place with animals in it.

Then — the flock atlas, about ten minutes of GPU. Sixteen flux_local bird sprites at ~5.7 s each → bg_remove_local → packed into one 1024 WebP 4×4 sheet. No TRELLIS, no meshes — a billboard needs an image, not geometry, so sixteen sprites cost two minutes where eight meshes would cost twenty-five. And it's not a new asset class: manifest.skins.facade is already twenty-five shipped, audited flux images used as textures. This single batch unlocks the whole +1-draw flock: cockatoo, galah, corella, lorikeet, perched magpie, ibis.

Then — the ibis prop, six minutes. flux_localbg_remove_localtrellis2_mlxnormalize, decimated to ≤450 tris, head down.

Everything above is unattended, zero-risk and gate-free in the sense that nothing existing can break.

If you want to go shopping anyway

Nothing in the plan blocks on it, and after the tier-2 cut there is no download on the critical path. But if you're browsing:

  1. Go to Quaternius first. Everything else is downstream of it. CC0, glTF included, low-poly single-material by design.
  2. Before writing a single SOURCES.md line, open the .gltf and confirm animations is non-empty. The number one silent failure of free packs is clips that live only in the .blend or .fbx while the .gltf ships an empty array. Fifteen minutes with tools we've already built: pipeline/animal_intake.py for the file checks, and the spike harness at scratchpad/spike_run.py — it takes a filename — to confirm a bare AnimationMixer actually moves a bone. If the clips aren't in the glTF, walk away. The pack's entire value to us is the walk cycle.
  3. The one thing genuinely worth grabbing while you're there is not an animal. Quaternius's "Universal Animation Library" — CC0, 130+ clips on a Mixamo-compatible humanoid rig. That's fuel for our human fleet's 8-clip bank, which is the actual thin spot. It touches rigs.js, which is classic-covenanted, so it's its own round with its own gates. Don't fold it into the animal work.
  4. Do not spend money. Not on TurboSquid, not on CGTrader, not on Sketchfab Store, not on a $50/mo auto-rigger. Their licences forbid serving an open .glb over plain HTTP, which is the only thing PROCITY does. Paying doesn't unlock them.

The two-sentence version

Yes, every animal ships its own bespoke skeleton with its own bone names, and that turns out to be completely fine — a self-contained GLB binds its own clips to its own bones through its own mixer, so retargeting, the thing that needs a shared skeleton, is exactly what we must not do. What actually limits animals is draw calls: a skinned animal can't be instanced so it costs one draw each against maybe twenty spare, while sixty birds on wires cost one draw between them — so generate a bird sprite sheet and a handful of animal sounds this week for $0, and buy nothing, because the one species that would have justified a download was a walking dog and our own spec had it tied up outside the shops.