What it does
game.visibility.create() makes a set, and you fill it with the level's walls: single segments, the four edges of a
rectangle, or the edges of a polygon. Each segment is a plain object, so moving a door or a crate is a matter of
changing its numbers, and remove and clear take walls away.
visibility(x, y, range) returns the visibility polygon: the points a viewer at (x, y) can see to, in angle order,
ready to fan into triangles and draw as a light or a fog mask. It uses the classic method, a ray cast just either side
of every segment corner with the nearest hit kept, which is exact and leaves no holes. canSee says whether the
straight line between two points is within range and crosses no wall, the question a guard asks about the player.
raycast returns the first hit along a ray, with its point and distance, for a hit-scan weapon or a laser.
Optional bounds are a rectangle every ray stops at, usually the camera's view, so a polygon in an open world stays
finite. The class, Visibility2d, is exported too, so a pack or a worker can use it without the game.
Features
- Visibility polygons — the fan of points a viewer sees within a range, in angle order.
- Line of sight —
canSeebetween two points, with a maximum distance. - Raycasts — the nearest hit point and its distance along any direction.
- Walls from shapes —
addSegment,addRectandaddPoly, from points or a flat list, closed or open. - Moving walls — change a segment's fields to move it, with nothing to rebuild.
- Bounds —
setBoundsstops rays at a rectangle, such as the camera's view.
Good for
- Stealth games: can the guard see the player, and what does the guard's view cover.
- Fog of war in a continuous world, drawing what the player can see as a mask.
- Shadowed 2D lights, through the lighting pack.
- Hit-scan weapons and lasers that stop at the first wall.
- Top-down shooters with free-form walls, where enemies react only to what they can see.
Make it yours
The pack has no options: a set is its walls and its bounds. Two different uses: a stealth level where each guard calls
canSee towards the player within 400 units every step, and the player's own view is drawn from visibility as a
darkness mask; and a lit scene where the same walls cast shadows from every light, with no fog at all.
A quick look
Walls of three kinds, then the three questions:
const v = game.visibility.create({ x: 0, y: 0, w: 640, h: 360 }); // bounds rays always stop at
v.addRect(100, 80, 32, 32); // four walls
v.addPoly([0, 0, 50, 10, 40, 60]); // a closed polygon
const wall = v.addSegment(0, 200, 640, 200); // one wall; change wall.x1 and the rest to move it
const poly = v.visibility(px, py, 300); // points in angle order within 300 units
const seen = v.canSee(guard.x, guard.y, hero.x, hero.y, 400);
const hit = v.raycast(ox, oy, dx, dy, 500); // { x, y, dist } of the nearest hit, or null
Works with
lighting-2d— shadowed point lights, which keep one of these sets as their occluders.stealth— guards with sight, hearing, suspicion and search, built on this pack.fog— a tile-grid fog of war that remembers what was seen, the alternative for tile maps.
Good to know
- The cost is rays times segments per call: hundreds of segments and a handful of polygons a frame is comfortable, and for big maps you merge runs of tiles into long segments.
- With no walls in range, no bounds and no range limit,
visibilityreturns an empty list: set bounds in an open world. canSeeignores the bounds, whileraycastandvisibilityinclude them.- Segments have no thickness and no inside, and there is no spatial index: every call scans every segment.
- It is 2D only.






