dot-skills Fog of War (JavaScript/TypeScript) Best Practices
Performance and correctness guide for fog of war and field-of-view systems in JS/TS games, distilled from the canonical FOV literature (Björn Bergström, Albert Ford, Adam Milazzo), Red Blob Games, rot.js, and the MDN/WebGL rendering APIs. Contains 44 rules across 8 categories, ordered by impact, to guide writing, reviewing, and refactoring visibility code.
When to Apply
Reference these guidelines when:
- Implementing field of view, line of sight, or tile visibility for a grid or continuous map
- Building or refactoring a fog-of-war display (unexplored / explored / visible layers)
- Diagnosing slow fog (recompute every frame), visual artifacts (flicker, light leaks), or memory blowups on large maps
- Scaling visibility to many units (RTS) or to large/streaming worlds
- Choosing how to store and render the visibility/explored state
Rule Categories by Priority
| Priority | Category | Impact | Prefix |
|---|---|---|---|
| 1 | FOV / Visibility Algorithm | CRITICAL | fov- |
| 2 | Update Scheduling & Incremental Recompute | CRITICAL | update- |
| 3 | State Representation & Data Structures | HIGH | state- |
| 4 | Rendering the Fog Layer | HIGH | render- |
| 5 | Memory & Allocation | MEDIUM-HIGH | mem- |
| 6 | Multi-Viewer & Map Scaling | MEDIUM | scale- |
| 7 | Geometry & Hot-Loop Math | MEDIUM | geo- |
| 8 | Correctness & Visual Artifacts | LOW-MEDIUM | correct- |
Quick Reference
1. FOV / Visibility Algorithm (CRITICAL)
fov-recursive-shadowcasting- Use recursive shadowcasting, not ray-per-cell FOVfov-symmetric-shadowcasting- Prefer symmetric shadowcasting for consistent visibilityfov-octant-transforms- Transform octants with a lookup table, not eight loopsfov-radius-bounded-scan- Bound the scan to the sight radius and map edgesfov-single-ray-los- Use a single line-of-sight ray for point-to-point checksfov-dda-continuous- Traverse continuous space with a DDA grid walkfov-visibility-polygon- Compute a visibility polygon for smooth 2D fog
2. Update Scheduling & Incremental Recompute (CRITICAL)
update-recompute-on-move- Recompute field of view only when the viewer movesupdate-dirty-flag- Track a dirty flag per viewer and a map version stampupdate-refcount-visibility- Count viewers per tile for incremental multi-viewer updatesupdate-delta-not-clear- Emit visibility deltas instead of clear-all-recomputeupdate-debounce-map-edits- Batch map edits and recompute affected viewers onceupdate-merge-explored- Merge visible into explored as you reveal, never rebuild it
3. State Representation & Data Structures (HIGH)
state-typed-arrays- Store fog state in typed arrays, not arrays of objectsstate-flat-1d-index- Index a flat buffer with y*width+x, not nested arraysstate-three-state-encoding- Encode the three fog states as bit flags in one bytestate-bitset-layers- Use a bitset for boolean visibility layersstate-row-major-iteration- Iterate row-major to match the buffer's memory layoutstate-no-string-keys- Avoid string-keyed maps for per-tile visibility
4. Rendering the Fog Layer (HIGH)
render-offscreen-fog-layer- Render fog to a separate offscreen layerrender-dirty-region-only- Repaint only the dirty fog regionrender-imagedata-not-fillrect- Build fog as one ImageData, not per-tile fillRectrender-lowres-soft-upscale- Render soft fog at tile resolution and upscale on the GPUrender-webgl-texsubimage- Upload only the dirty rect of the fog texture in WebGLrender-fade-alpha-lerp- Animate fog reveal by lerping alpha, not recomputing FOV
5. Memory & Allocation (MEDIUM-HIGH)
mem-reuse-buffers- Allocate fog buffers once and reuse themmem-clear-with-fill- Clear the visible buffer with fill, not reallocation or a loopmem-generation-stamp- Use a generation stamp to skip the per-frame clearmem-bitpack-explored- Pack the explored layer into bits for memory and savesmem-no-hot-loop-closures- Hoist allocations out of the FOV scan loop
6. Multi-Viewer & Map Scaling (MEDIUM)
scale-chunk-large-maps- Chunk large maps and keep only active chunks residentscale-shared-team-visibility- Share one refcounted visibility buffer per teamscale-spatial-partition-edits- Find edit-affected viewers with a spatial indexscale-gameplay-vs-render-culling- Separate gameplay visibility from on-screen render cullingscale-cap-recompute-budget- Cap FOV recomputes per frame with a time budget
7. Geometry & Hot-Loop Math (MEDIUM)
geo-squared-distance- Compare squared distances to avoid per-cell sqrtgeo-avoid-modulo-deindex- Track x and y directly instead of modulo-deindexinggeo-integer-slope-shadows- Use rational slopes to avoid floating-point driftgeo-avoid-trig-in-loop- Precompute directions instead of calling trig per cellgeo-radius-shape-choice- Choose the radius metric for the shape you want
8. Correctness & Visual Artifacts (LOW-MEDIUM)
correct-symmetric-walls- Light wall tiles consistently to avoid flickering facescorrect-corner-peeking- Handle diagonal wall corners deliberatelycorrect-permissiveness-model- Pick one permissiveness model and apply it everywherecorrect-explored-not-overwritten- Never let the visible pass overwrite the explored layer
How to Use
Read individual reference files for the full explanation and incorrect/correct code:
- Start with
fov-andupdate-for the two biggest wins: a correct algorithm and recomputing only on change. - Section definitions - Category structure, impact levels, and the execution-lifecycle ordering.
- Rule template - Template for adding new rules.
Reference Files
| File | Description |
|---|---|
| references/_sections.md | Category definitions and impact ordering |
| assets/templates/_template.md | Template for new rules |
| metadata.json | Version and reference information |