bridge-sprite-baking.ts
Pure rendering helpers for sprite outline baking and fog stamp generation. No engine state dependency — every export is a deterministic-input pure function (modulo Math.random for fog noise).
Purpose
Pre-bake atmospheric and outline visuals to per-sprite HTMLCanvasElement textures so the hot render path only does drawImage calls. Two unrelated utilities live here:
- Fog stamp — a single 256×256 noise texture reused as parallax atmospheric overlay.
- Outlined sprite baking — wraps an arbitrary sprite with a crisp circular stroke and optional rarity glow.
Exports
| Symbol | Kind | Returns |
|---|---|---|
getFogStamp() | function | HTMLCanvasElement (memoized 256×256) |
bakeOutlinedSprite(img, sz, strokeColor, glowColor, strokePx?) | function | HTMLCanvasElement (sized sz×sz) |
Fog stamp
getFogStamp() is memoized via module-scoped _fogStampCache; first call generates, subsequent calls return the cached canvas.
| Knob | Value |
|---|---|
| Size | sz = 256 |
| Background | solid #000000 fill |
| Blob count | 12 overlapping radial gradients |
| Blob radius | 40 + Math.random() * 60 |
| Blob center | uniform random over [0, 256) for both axes |
| Gradient stops | 0 → rgba(255,255,255,0.4), 0.6 → rgba(255,255,255,0.15), 1 → rgba(255,255,255,0) |
Intended use: drawn 1–3 times per frame as a parallax overlay. The randomness is unseeded — each page load produces a different noise pattern.
Outlined sprite baking
bakeOutlinedSprite(img, sz, strokeColor, glowColor, strokePx = 2) returns a fresh canvas with three composited layers (back → front):
| Layer | Content | Implementation |
|---|---|---|
| 0a | Outer glow ring | dilate(strokePx + 5, 32, glowColor, 0.22) — only if glowColor non-null |
| 0b | Inner glow ring | dilate(strokePx + 3, 32, glowColor, 0.38) — only if glowColor non-null |
| 1 | Crisp stroke | dilate(strokePx, 32, strokeColor, 1.0) |
| 2 | Original sprite | drawImage(img, pad, pad, drawSz, drawSz) |
Geometry constants
| Constant | Value | Purpose |
|---|---|---|
pad | 14 | Reserved border for stroke + glow spread; covers strokePx up to ~7 |
drawSz | sz - pad * 2 | Inner draw region for original + dilated copies |
Default strokePx | 2 | Crisp 2-pixel outline |
Dilation samples | 32 | Copies stamped on circle — eliminates octagonal artifact of cardinal-only stamping |
Dilation algorithm
dilate(radius, samples, color, alpha) is a local closure that:
- Creates an offscreen
layerCvsof sizesz×sz. - Draws
samplescopies ofimgaround a circle of givenradius, each at angle(i / samples) * 2π, offset from(pad, pad). - Sets
globalCompositeOperation = 'source-in'and floodscolor— recolors the silhouette to a single solid color. - Composites the result onto the output
ctxatalpha.
Net effect: produces a solid-color dilated silhouette of the source sprite without ever computing actual paths. Equivalent to a circular morphological dilation by radius pixels.
Caller contracts
- Pass
glowColor = nullto skip the glow layers (common enemies). - Pass the same color for
strokeColorandglowColorto get a unified rarity tint. imgmay be eitherHTMLImageElementorHTMLCanvasElement.strokePxhigher than ~7 will overflow the 14-pixel pad and clip the outline.
Performance notes
- Both functions allocate canvases — call once at bake time, never per frame.
- Outlined-sprite bake cost scales as
O((1 + 2·glow) · 32)drawImagecalls = 32 calls without glow, 96 with glow. - Fog stamp generation is one-shot; the memoization makes repeated
getFogStamp()calls O(1).
Dependencies
None. Imports nothing — uses only DOM canvas APIs.
EXTRACT-CANDIDATE
- The
pad = 14magic number ties tostrokePx ≤ 7. Could derivepad = strokePx + 6to remove the implicit cap and the comment-only invariant. dilatesamples count32is duplicated across all three call sites — could be a named constantDILATION_SAMPLES = 32.- Glow alpha pair
(0.22, 0.38)and radius offsets(+5, +3)are magic numbers; could move to aGLOW_LAYERStable for tunability. - Fog stamp constants (
sz=256,blobCount=12, radius range, gradient stops) could move to aFOG_STAMP_CONFIGdata object — currently the only knob is editing the function body. Math.random()in fog stamp is unseeded — if deterministic builds become a requirement, accept an optional RNG parameter.