Phaser AE

Vector Lines

Free packv3.0.0-62DLook

A line engine, not a draw-a-line call: masses of independent segments in one instanced draw, crisp at any scale, with the additive phosphor halo of a vector monitor. Ships, rocks, trails, star fields, text and HUDs all ride the same draw, and any outline can shatter in one call.

This is a Free pack. Every phaser.io account can use it in their games.

Released October 14, 2026 · updated October 20, 2026

Vector Lines: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

The pack

vector2d

function vector2d(opts: Vector2dOptions = {}): Pack<{ vector: Vector2dApi }>

The vector-2d pack: retained and immediate line art with a phosphor halo, solid faces, trails, starfields and line-art particles — one instanced draw for the lot. Supports the WebGPU and WebGL2 backends.

Helpers

resolveVecColor

function resolveVecColor(src: VecColorSource, i: number): string

Resolve a colour source for item i. Pure.

Functions

buildFill

function buildFill(points: readonly VecPt[], fill: VecFillSource | undefined): number[]

An outline turned into a triangle list: x, y, r, g, b, a per vertex, in the shape's own space.

Returns an empty list — not a white face — for anything that means "no fill". 'transparent', 'none', a fully transparent colour and fewer than three points all take that path.

buildFillWgsl

function buildFillWgsl(): string

The FILL shader — solid faces under the line art. A triangulated outline, one vertex at a time: a position in the shape's own space, its transform slot, a depth and a colour PER VERTEX, which is what gives the gradient with no extra uniform and no second pass. It shares the line pass's transform table, so a filled shape moves for the same eight floats.

DEPTH is the point of it. The fill pass WRITES depth where the line pass only tests it, so a nearer filled shape hides both the fills and the lines behind it — corridors, hulls, a web with solid lanes.

buildLineWgsl

function buildLineWgsl(stamp = false): string

Assemble the line shader. stamp picks the glow-stamp variant. Pure string work, so a test can read the two variants without a GPU.

centroid

function centroid(points: readonly VecPt[]): VecPt

The centroid of a path — where a shatter flies away from.

chainJoins

function chainJoins(points: readonly VecPt[], closed = false): Float32Array

segJoins for a path rather than a segment list.

clipToScissor

function clipToScissor( clip: VecClipRect, viewX: number, viewY: number, viewW: number, viewH: number, pxW: number, pxH: number, ): { x: number; y: number; w: number; h: number } | null

A world clip rect turned into an integer pixel SCISSOR on a pxW × pxH target, given the view rect the layer drew with. null means none of the rect is on the target, so the whole draw can be skipped.

Pure, and that is the point: the HALF-RES glow target runs the same arithmetic against its own size. A halo clipped to a different rectangle than the line that cast it is the bug this exists to prevent.

fragmentAlpha

function fragmentAlpha(age: number, life: number): number

Fragment alpha over age: hold, then fade the last 40%. A piece that fades from the instant it spawns reads as weak.

outlineSegs

function outlineSegs(shape: { parts?: ReadonlyArray<{ x: number; y: number; rot: number; scale: number; segs: ReadonlyArray<readonly [VecPt, VecPt]> }>; segs?: ReadonlyArray<readonly [VecPt, VecPt]>; }): Array<[VecPt, VecPt]>

Every segment of a shape in the SHAPE's own space, each part's local transform already baked in. The one place anything outside the renderer should read a shape's geometry — physics binds to shapes in headless tests where there is no device to build a real one.

particleOutline

function particleOutline(kind: VecShapeKind, circleSides = 10): Array<[VecPt, VecPt]>

The outline for one particle kind, centred on the origin at RADIUS 1 — the per-particle size is its scale.

partToShape

function partToShape(part: { x: number; y: number; rot: number; scale: number }, p: VecPt): VecPt

A point in a PART's own space, lifted into its SHAPE's space.

polySegs

function polySegs(points: readonly VecPt[], closed = false): Array<[VecPt, VecPt]>

Expand a polyline into segment endpoint pairs; closed joins the last point back to the first.

rampColor

function rampColor(ramp: VecRamp, t: number, cyclic: boolean): string

The colour of a ramp at t along it, 0..1. A CYCLIC ramp — a closed outline's — wraps from its last stop back to its first, so the join where the path closes has no seam; an open one runs first to last. t outside 0..1 wraps either way, which is what lets a shifted ramp run round a path for ever.

segJoins

function segJoins(segs: ReadonlyArray<readonly [VecPt, VecPt]>): Float32Array

Where each segment should be CUT so neighbours abut instead of overlapping — four floats a segment, in the same order polySegs produced them.

Each segment end carries the inward normal of the plane it is cut on. The two segments meeting at a joint share ONE plane — the angle bisector — and take opposite sides of it. Between them they still tile the whole join, each contributing half of the round cap, so the picture is unchanged while every pixel is painted exactly once.

That is the difference between this and cutting each segment square across its own end, which is the obvious thing to try and NOTCHES every corner: two perpendicular cuts leave an uncovered wedge between them, where one shared plane has no space between its two sides.

Painting once is what makes ALPHA work on an outline. Overlapping caps blend twice, so at alpha 0.35 a joint reaches 1 − 0.65² = 0.58 while the rest of the line sits at 0.35, and every corner beads. Fully opaque lines never showed it, because drawing the same colour over itself changes nothing.

A free end — either end of an open chain — gets a zero normal, which the shader reads as "no cut" and leaves as a round cap.

shapeOrigin

function shapeOrigin(s: { x: number; y: number; rot: number; scale: number; pivot: VecPt | null }): VecPt

Where a shape's transform actually puts its origin, with pivot folded in.

The shader only knows world = R·S·local + T; there is no pivot in it. Turning about an arbitrary point is that same transform with a different T, so the whole feature lives here and costs nothing per vertex:

world(p) = R·S·(p − pivot) + pivot + xy  =  R·S·p + (xy + pivot − R·S·pivot)

The pivot itself therefore lands at exactly (x + pivot.x, y + pivot.y) — which is the point a shatter should burst from.

shatter

function shatter(segs: ReadonlyArray<readonly [VecPt, VecPt]>, opts: { speed?: number; spin?: number; life?: number; rnd?: () => number } = {}): Shard[]

Break an outline into shards, each flying away from the centroid with a spin. The speed is scaled by how far the piece already was from the middle, so a long shape comes apart lengthways rather than as a uniform puff.

spawnFragment

function spawnFragment( x: number, y: number, dirX: number, dirY: number, opts: { speed?: number; spin?: number; life?: number }, rand: () => number, ): VecFragment

Spawn kinematics for one shattered segment. The transform sits ON the segment's world MIDPOINT, so its spin turns the piece about its own centre rather than orbiting the shape's origin, and it flies along the outward unit direction with jitter and spin from rand.

stepFragment

function stepFragment(f: VecFragment, dt: number, drag = 0.4, gravity = 0): boolean

Advance one fragment. Returns false once it has expired.

stepShard

function stepShard(s: Shard, dt: number, drag = 0.6): boolean

Advance a shard. Returns false once it has lived its life.

vectorStampRadius

function vectorStampRadius(width: number, glow: number, glowWidth?: number): number

A segment's halo radius, world units: 0 when glow is 0 or less; width/2 + 1 + glow*2 with no glowWidth; else width/2 times glowWidth (at least 1).

worldToLocal

function worldToLocal( s: { x: number; y: number; rot: number; scale: number; pivot: VecPt | null }, wx: number, wy: number, ): VecPt | null

A WORLD point mapped into a shape's LOCAL space — the inverse of the transform the shader applies.

Doing it this way round is the whole performance story: transforming the OUTLINE is O(n) per shape per query, while inverting the POINT is O(1) and the outline is then tested exactly as it was authored.

With c = cos(rot)·scale and n = sin(rot)·scale, R·S is [[c, −n], [n, c]], whose determinant is scale², so the inverse is [[c, n], [−n, c]] / scale². Null for a degenerate scale of 0 — the shape occupies no area, so nothing can be inside it.

Classes

VecStarfield

class VecStarfield

A drifting or warping star field. Every public field is live.

  • mode: VecStarMode — 'drift' scrolls a flat field; 'warp' flies into the screen.
  • bounds: { x: number; y: number; w: number; h: number } | null — The rectangle the field lives in, or null to follow the layer's view.
  • speed: number — DRIFT: world units each second for the nearest layer. WARP: depth units each second.
  • angle: number — DRIFT: the direction of travel, radians.
  • origin: { x: number; y: number } | null — WARP: the vanishing point, or null for the centre of the rect.
  • focal: number — WARP: the focal length.
  • near: number — WARP: the nearest depth before a star recycles.
  • far: number — WARP: the depth stars spawn at.
  • bend: { x: number; y: number } | ((t: number, time: number) => { x: number; y: number }) | null — WARP: a constant lean, or a function for a curved tunnel.
  • spin: number — WARP: the roll about origin, radians each second.
  • size: number — The star size at the nearest depth.
  • streak: number — The tail length, in seconds of travel.
  • color: string — The star colour.
  • colors: VecColorSource | null — A colour source indexed per star, or null.
  • glow: number — The halo intensity.
  • z: number | undefined — The depth for the vector pass.
  • dead: boolean — True after kill(). The layer drops a dead field.
  • readonly stars: VecStar[] — Every star, live. Move them, cull them, sort them — it is an array.
  • each: ((s: VecStar, dt: number, field: VecStarfield) => void) | null — Run over every star each step, after the built-in motion and before the draw.
  • get layers(): number — DRIFT parallax layers. Assign at any time; the field re-layers on the spot. Sets the layer count, rounded to a whole number and floored at 0; 0 or 1 gives one flat layer. Stars are re-dealt to layers immediately.
  • set layers(n: number) — DRIFT parallax layers. Assign at any time; the field re-layers on the spot. Sets the layer count, rounded to a whole number and floored at 0; 0 or 1 gives one flat layer. Stars are re-dealt to layers immediately.
  • constructor(private host: StarHost, o: VecStarfieldOptions = {}) — Built through game.vector.starfield().
  • get rect(): { x: number; y: number; w: number; h: number } — The rectangle in play — the explicit bounds, else the live view.
  • resize(count: number): void — Grow or shrink the field, keeping the stars already placed.
  • reset(): void — Re-scatter every star — a new level, a jump, a different sky.
  • kill(): void — Remove the field from the layer.
  • step(dt: number): void — Move every star on and recycle the ones that leave. The layer calls it each frame.
  • emit(): void — Push the field's segments at the host. The layer calls it each frame.

VecTrail

class VecTrail

A ribbon behind a moving thing. Every field is live: change any of them between frames.

  • length: number — Samples kept.
  • minDist: number — Minimum world distance between samples.
  • maxDist: number — The longest move one sample may span; 0 is off.
  • life: number — Seconds a sample lives; 0 is length-bound only.
  • width: number — Stroke width at the head.
  • tailWidth: number — Stroke width at the tail.
  • alpha: number — Alpha at the head.
  • tailAlpha: number — Alpha at the tail.
  • color: string — Colour at the head.
  • tailColor: string | null — Colour to grade toward at the tail, or null for one colour.
  • colors: VecColorSource | null — A colour source stepped along the ribbon, or null.
  • glow: number — Halo intensity at the head.
  • dwell: number — Beam dwell 0..1.
  • z: number | undefined — Depth.
  • dead: boolean — True after kill(). The layer drops a dead trail.
  • constructor(private host: TrailHost, o: VecTrailOptions = {}) — Built through game.vector.trail().
  • get count(): number — How many samples it is holding.
  • get points(): readonly VecPt[] — The samples, oldest first.
  • push(x: number, y: number): void — Feed it the mover's position — once a frame is right.
  • clear(): void — Drop every sample — a teleport, so the ribbon does not smear across it.
  • kill(): void — Stop drawing it and let the layer forget it.
  • step(dt: number): void — Age the samples out. The layer calls it; call it yourself only off-layer.
  • emit(): void — Push the ribbon's segments at the host. The layer calls it each frame.

Constants

FILL_FLOATS

const FILL_FLOATS: 8

Floats one filled vertex packs to: position with depth and slot, then rgba.

FILL_WGSL

const FILL_WGSL: string

The fill shader, built once and exported so a test can read it and a fill pass can share the transform table.

LINE_FLOATS

const LINE_FLOATS: 24

Floats one segment instance packs to: six vec4s.

resolvePoints

const resolvePoints = (p: VecPoints): readonly VecPt[]=> …

Resolve a point source to its array.

XFORM_FLOATS

const XFORM_FLOATS: 8

Floats one transform slot packs to: two vec4s.

Types

Shard

interface Shard

One piece of a shattered outline: its own transform, drifting and spinning away.

  • x: number — Where the piece is now, relative to the shape's own origin.
  • y: number — And its y.
  • vx: number — Its drift, world units a second.
  • vy: number — And its y drift.
  • spin: number — Its turn, radians a second.
  • rot: number — Its rotation now.
  • age: number — Seconds lived.
  • life: number — How long it lives.

StarHost

interface StarHost

Where a starfield pushes its segments, and the view it lays out against.

  • seg(x0: number, y0: number, x1: number, y1: number, style: VecStylePatch): void — Pushes one line segment from (x0, y0) to (x1, y1), in world units, drawn with the given style; a zero-length segment is a dot.
  • readonly viewRect: { x: number; y: number; w: number; h: number } — The view being drawn, so an unbounded field follows a resize.

TrailHost

interface TrailHost

Where a trail pushes its segments — the layer, or a test double.

  • seg(x0: number, y0: number, x1: number, y1: number, style: VecStylePatch): void — Pushes one line segment from (x0, y0) to (x1, y1), in world units, drawn with the given style.

VecClipRect

interface VecClipRect

A clip rectangle, in WORLD units.

  • x: number — The left edge.
  • y: number — The top edge.
  • w: number — The width.
  • h: number — The height.

VecColorSource

type VecColorSource = | 'rainbow' | (string & {}) | readonly string[] | ((i: number) => string) | { hue: readonly [number, number]; step?: number; sat?: number; val?: number } | { from: string; to: string; step?: number }

Where a per-item colour comes from.

  • 'rainbow' — walk the hue wheel
  • any other string — one flat colour
  • string[] — cycle an explicit palette
  • (i) => string — anything else; i is the item's index in the run
  • { hue: [from, to], step?, sat?, val? } — an arc of the wheel, walked and folded so it cycles
  • { from, to, step? } — ping-pong between two literal colours, so a run never jumps

step is DEGREES PER ITEM and defaults to 1, so a burst of count spans count × step degrees. Raise it to fan one burst across the wheel; leave it at 1 for a stream that cycles gradually.

VecEmitOptions

interface VecEmitOptions

A burst of line-art particles.

  • x: number — The x centre of the burst. Required.
  • y: number — The y centre of the burst. Required.
  • count?: number — Particles in the burst. Default 12.
  • shape?: VecShapeKind | readonly VecShapeKind[] — The outline to cut them from — one kind, or an array to mix per particle. Default 'square'.
  • size?: number — Radius in world units. Default 6.
  • sizeVar?: number — Random size variation, ± world units. Default 2.
  • speed?: number — Initial speed. Default 90.
  • speedVar?: number — Random speed variation, ± world units each second. Default 40.
  • angle?: number — Direction, radians. Default 0.
  • spread?: number — The arc the directions spread across, radians. Default a full circle.
  • even?: boolean — Space the angles EVENLY across the spread — a clean ring or fan rather than a scatter.
  • spawnRadius?: number — Jitter the spawn point within this radius. Default 0.
  • spin?: number — Random spin up to ±spin rad/s. Default 3.
  • life?: number — Seconds. Default 1.2.
  • lifeVar?: number — Random life variation, ± seconds. Default 0.4.
  • drag?: number — Velocity damping each second. Default 0.4.
  • gravity?: number — Downward acceleration. Default 0; negative rises.
  • shrink?: number — Final size as a fraction of the start. Default 1; 0 shrinks to nothing.
  • style?: VecStyle — The line look — the same style a shape takes.
  • colors?: VecColorSource — A colour per particle. The cursor lives on the LAYER, so a per-frame stream keeps walking the wheel.
  • circleSides?: number — Segments per circle. Default 10. Fewer is chunkier and cheaper.

VecFaceOptions

interface VecFaceOptions

What an immediate FACE takes. No width and no glow: a face has neither, and offering them would lie.

  • alpha?: number — Multiplies the face colour's own alpha, exactly as it does for a stroke. Default 1.
  • z?: number — Depth within the layer, 0..1. Default 0.
  • screen?: boolean — Draw in SCREEN space rather than world space. Default false.

VecFill

interface VecFill

A solid face under an outline. A bare string is a flat colour; an array is a multi-stop ramp.

  • color?: string — The face colour, or the first stop of a ramp. 'transparent' and 'none' mean no face at all.
  • to?: string — The far end of a two-stop ramp.
  • stops?: readonly string[] — Three or more stops, evenly spaced along the ramp. Beats color/to.
  • angle?: number — Ramp direction in SHAPE-LOCAL radians. Default 0, left to right.
  • radial?: boolean — Ramp outward from the centre instead of along an axis.

VecFillSource

type VecFillSource = string | readonly string[] | VecFill | null

Anything a caller may pass where a fill is wanted.

VecFragment

interface VecFragment

One drifting, spinning piece — a shard of a shattered outline, or a particle from a burst.

  • x: number — World x.
  • y: number — World y.
  • rot: number — Rotation, radians.
  • scale: number — Scale factor.
  • vx: number — World units each second, x.
  • vy: number — World units each second, y.
  • spin: number — Radians each second.
  • age: number — Seconds lived so far.
  • life: number — Total seconds it lives.

VecHitOptions

interface VecHitOptions

How VecShape.contains decides a hit.

  • tolerance?: number — Slop in WORLD units, added around the stroke. Default 6 — about a fingertip at a normal world height. A 2-unit line is otherwise nearly impossible to click.
  • mode?: 'auto' | 'fill' | 'stroke' — 'auto' (default) — inside a CLOSED outline counts, and so does being within tolerance of the stroke. An OPEN polyline has no interior, so it is stroke-only. This is what a drag handle wants. 'fill' — interior only; a closed outline behaves like a solid face and the stroke is not separately grabbable. An open path never hits. 'stroke' — proximity to the line only; the middle of a closed shape is a miss. This is the Star Castle case: a shot passes THROUGH the ring and kills only the section it actually crossed.

VecPartHandle

interface VecPartHandle

One live part of a shape.

  • x: number — Offset within the shape. Live.
  • y: number — Offset within the shape. Live.
  • rot: number — Turned on top of the shape's rotation. Live.
  • scale: number — Multiplied with the shape's scale. Live.
  • readonly name: string | null — Its handle, or null.
  • setPoints(points: VecPoints, closed?: boolean): void — Replace this part's outline.
  • readonly points: readonly VecPt[] — The points it holds.
  • restyle(style: Partial<VecStyle> & { fill?: VecFillSource; ramp?: VecRamp | null; rampShift?: number }): void — Restyle just this part.
  • slotFor(origin: VecPt, c: number, n: number): { x: number; y: number; rot: number; scale: number } — WHERE THIS PART ACTUALLY SITS, given the shape's world origin and its rotation folded with its scale as c = cos(rot) * scale and n = sin(rot) * scale. The renderer's own slot maths, exposed — a debug overlay that recomputes it by hand is a second copy that will disagree.

VecPartOptions

interface VecPartOptions extends VecStyle

One outline inside a MULTI-PART shape.

A part carries its own geometry, its own style, its own fill and its own place within the shape. Anything it does not set falls back to the shape's, so parts that share a look say nothing. This is how a lander gets a hull, a flame and two legs that all turn together but recolour separately.

  • points: VecPoints — This part's outline — an array, or a factory that returns one.
  • fill?: VecFillSource — This part's solid face. Falls back to the shape's.
  • closed?: boolean — Does the outline close? Default true.
  • x?: number — Offset within the shape, in the shape's own point space.
  • y?: number — The y offset within the shape.
  • rot?: number — Turned about the part's own origin, on top of the shape's rotation.
  • scale?: number — Multiplied with the shape's scale.
  • name?: string — A handle, so shape.part('leftLeg') beats an array index.
  • ramp?: VecRamp | null — A colour run along this part's WHOLE outline. Falls back to the shape's. See VecShapeOptions.ramp.
  • rampShift?: number — Turns the ramp along the outline, 0..1. Falls back to the shape's.

VecPoints

type VecPoints = readonly VecPt[] | (() => readonly VecPt[])

Points, or something that makes them.

Every place that takes an outline takes this, so a shape factory goes straight in — shape(() => star(5, 40, 18)) — with no array built first. It is called ONCE, when the outline is built; it is not a per-frame hook.

VecPt

interface VecPt

A point on a path.

  • x: number — Horizontal position in world units (pixels at the default view); grows rightward.
  • y: number — Vertical position in world units (pixels at the default view); grows downward.

VecRamp

type VecRamp = readonly string[] | 'rainbow'

A colour run along a whole outline: stops evenly spaced along it, or the hue wheel.

VecShape

interface VecShape

A retained shape: one or more outlines under one transform.

  • x: number — Position x. Live.
  • y: number — Position y. Live.
  • rot: number — Rotation, radians. Live.
  • scale: number — Uniform scale. Live.
  • alpha: number — A multiplier over every segment's alpha. Live.
  • glow: number — A multiplier over every segment's glow. Live.
  • visible: boolean — Whether it draws at all.
  • pivot: VecPt | null — The point it turns and scales about, in point space, or null for the point origin. Live.
  • wrap: { w: number; h: number } | null — The wrap size, or null. Live — set or clear it at any time.
  • setPoints(points: VecPoints, closed?: boolean): void — Replace the outline — re-packs only this shape, so a crater can be blown in a terrain.
  • readonly points: readonly VecPt[] — The points it currently holds — the FIRST part's, on a multi-part shape.
  • readonly parts: readonly VecPartHandle[] — Its parts, in order. A single-outline shape has exactly one.
  • part(name: string): VecPartHandle | null — One part by name, or null.
  • restyle(style: Partial<VecStyle> & { fill?: VecFillSource; ramp?: VecRamp | null; rampShift?: number }): void — Change the look without rebuilding the geometry.
  • contains(wx: number, wy: number, opts?: VecHitOptions): boolean — Is a world point on or inside it? Ghost copies count.
  • bounds(): { x: number; y: number; w: number; h: number } — Its world-space bounding box, from the points themselves rather than a rotated local box.
  • shatter(opts?: { speed?: number; spin?: number; life?: number; drag?: number }): void — BREAK IT APART. Every segment re-parents to its own drifting, spinning transform and fades over life, then the shape removes itself. The death of every rock, ship and shield in the vector canon. drag (default 0.6) is how fast the pieces slow down: turn it up and the debris settles close in, turn it down and it keeps going.
  • readonly shattering: boolean — True while it is shattering.
  • kill(): void — Remove it.
  • revive(): this — Put a killed shape back, un-shattered.
  • readonly alive: boolean — True until killed.

VecShapeKind

type VecShapeKind = 'line' | 'triangle' | 'square' | 'diamond' | 'circle' | 'cross' | 'star'

The outline a vector particle is cut from.

VecShapeOptions

interface VecShapeOptions extends VecStyle

Everything shape() takes: the line style, plus the transform, the face, the pivot and the wrap.

  • x?: number — World x, measured from the pivot. Default 0.
  • y?: number — World y, measured from the pivot. Default 0.
  • rot?: number — Rotation about the pivot, radians. Default 0.
  • scale?: number — Scale about the pivot. Default 1.
  • closed?: boolean — Join the last point back to the first. Default true.
  • fill?: VecFillSource — A solid face under the outline.
  • pivot?: VecPt | null — The point the shape turns and scales about, in POINT space. Null (the default) is the point origin. It costs nothing per vertex — see shapeOrigin.
  • wrap?: { w: number; h: number } | null — Draw three GHOST copies at ±w, ±h — the Asteroids wrap, where a rock straddling the edge appears on both sides at once. Keep the position itself in range yourself; this only draws the copies.
  • ramp?: VecRamp | null — A colour run along the WHOLE outline by arc length — stops evenly spaced from the first point round the path, or 'rainbow' for the hue wheel. color2 grades each SEGMENT on its own, so on a chain it repeats at every joint and reads as banding; a ramp grades the path once. A closed outline's ramp wraps back to its first stop, so there is no seam. Beats color / color2 on the stroke.
  • rampShift?: number — Turns the ramp along the outline, 0..1. Restyle it each frame for a running colour cycle. Default 0.

VecStar

interface VecStar

One star.

  • x: number — DRIFT: world x. WARP: lateral offset from the tunnel axis, before projection.
  • y: number — DRIFT: world y. WARP: vertical offset from the tunnel axis, before projection.
  • z: number — DRIFT: which layer, 0 (nearest, full speed) to 1 (farthest). WARP: depth in world units.

VecStarfieldOptions

interface VecStarfieldOptions

Tuning for game.vector.starfield().

  • count?: number — How many stars. Default 240.
  • mode?: VecStarMode — Default 'drift'.
  • bounds?: { x: number; y: number; w: number; h: number } | null — The rectangle the field lives in. Default: the layer's current view, so it follows a resize.
  • speed?: number — DRIFT: world units each second for the nearest layer. WARP: depth units each second. Negative reverses. Default 60.
  • angle?: number — DRIFT: direction of travel, radians. 0 is right, +y is down. Default 0.
  • layers?: number — DRIFT: parallax layers. Default 4; 0 or 1 makes one flat layer.
  • origin?: { x: number; y: number } | null — WARP: the vanishing point. Default the centre of bounds.
  • focal?: number — WARP: focal length in world units. Default 260; bigger is a narrower field.
  • near?: number — WARP: nearest depth before a star recycles. Default 12.
  • far?: number — WARP: the depth stars spawn at. Default 900.
  • bend?: { x: number; y: number } | ((t: number, time: number) => { x: number; y: number }) | null — WARP: a constant sideways lean, or (t, time) => offset for a curved tunnel.
  • spin?: number — WARP: roll the whole field about origin, radians each second.
  • size?: number — Star size at the nearest depth. Default 2.2.
  • streak?: number — Tail length, in SECONDS of travel. 0 (the default) draws plain dots.
  • color?: string — Star colour. Default white.
  • colors?: VecColorSource | null — A colour source indexed per star, for a mixed-temperature sky. Resolved when a star spawns.
  • glow?: number — Halo intensity. Default 0.5.
  • z?: number — Depth for the vector pass. Default: behind everything else in the layer.
  • rng?: () => number — Deterministic layouts — pass the game's seeded rng.

VecStarMode

type VecStarMode = 'drift' | 'warp'

'drift' scrolls a flat field; 'warp' flies into the screen.

VecStyle

interface VecStyle

How a shape or a segment is drawn.

  • color?: string — Colour, css. Default white.
  • color2?: string — A second colour, so the line runs as a gradient from color to this.
  • alpha?: number — Opacity 0..1. Default 1.
  • width?: number — Core width in world units. Under 1 it is a HAIRLINE and joins are cut square. Default 1.5.
  • glow?: number — Phosphor halo brightness, 0 for a bare crisp line. Default 0.
  • glowWidth?: number — Halo radius as a multiple of the core's half-width, at least 1. Omitted: the radius is width/2 + 1 + glow*2.
  • dash?: number — Dash period in world units, 0 for solid. Default 0.
  • dwell?: number — BEAM DWELL 0..1 — how much the ends of a stroke burn brighter and fatter, as a real display's beam decelerates into a turn. Scaled per end by how sharply the line actually turns. Default 0.
  • z?: number — Depth 0..1; a larger number draws in front. Default 0.
  • screen?: boolean — Draw in SCREEN pixels rather than world units — a HUD, a joystick, a hotbar. Immediate only (seg, poly, arc, text): a HUD is redrawn every frame anyway, and a retained screen shape would want its own transform table for no gain. Retained shapes, bursts, trails and star fields ignore it.

VecStylePatch

type VecStylePatch = { [K in keyof VecStyle]: VecStyle[K] | undefined }

A style whose fields may be CLEARED as well as set.

Anything that reuses ONE style object across many segments — a trail's ribbon, a starfield's stars — needs to unset a field between them (color2 on a graded trail is the case that forces it), and exactOptionalPropertyTypes refuses undefined on a plain optional field. Reusing the object is the point: a thousand stars then cost no allocation at all.

Vector2dApi

interface Vector2dApi

game.vector.

  • shape(points: VecPoints | readonly VecPartOptions[], style?: VecShapeOptions): VecShape — A retained shape from a path, or from a list of PARTS.
  • seg(x1: number, y1: number, x2: number, y2: number, style?: VecStyle): void — One segment, this frame only.
  • poly(points: readonly VecPt[], style?: VecStyle & { closed?: boolean }): void — A path, this frame only.
  • arc(cx: number, cy: number, r: number, from: number, to: number, style?: VecStyle, steps?: number): void — An arc, this frame only. from and to are RADIANS — a full circle is 0, Math.PI * 2, and 360 here is fifty-seven turns.
  • fill(points: readonly VecPt[], fill: VecFillSource, opts?: VecFaceOptions): void — A SOLID FACE, this frame only — the filled counterpart of poly. Flat colour, a two-stop ramp or a multi-stop one, exactly as a retained shape's fill takes. Reach for it whenever the face is TRANSLUCENT. The old idiom for a filled disc — an arc of radius r/2 stroked r wide — is a fan of segments that overlap heavily toward the centre, so a face asked for at 7% alpha composites with itself forty times over and comes out solid. It is exact at alpha 1 and wrong at every other alpha, with nothing said. This is one layer of triangles at the alpha asked for.
  • disc(cx: number, cy: number, r: number, fill: VecFillSource, opts?: VecFaceOptions, steps?: number): void — A filled circle, this frame only — fill() over a circle of steps segments (default 48).
  • text(str: string, x: number, y: number, opts?: VecTextOptions & { style?: VecStyle }): void — STROKED TEXT, this frame only — the vector font, drawn as line art like everything else.
  • emit(opts: VecEmitOptions): void — A burst of line-art particles.
  • trail(opts?: VecTrailOptions): VecTrail — A ribbon behind a moving thing.
  • starfield(opts?: VecStarfieldOptions): VecStarfield — A drifting or warping star field.
  • hit(wx: number, wy: number, opts?: VecHitOptions): VecShape | null — The topmost shape under a world point, or null.
  • hitAll(wx: number, wy: number, opts?: VecHitOptions): VecShape[] — Every shape under a world point, topmost first.
  • readonly all: readonly VecShape[] — Every live shape.
  • clear(): void — Remove them all — shapes, trails, fields and live particles.
  • clip: VecClipRect | null — Restrict the layer to a world rectangle, or null for none. Live.
  • glow: number — A multiplier over every glow in the layer. Live.
  • glowSize: number — How far the halo reaches, logical pixels. Live.
  • enabled: boolean — Turn the layer off.
  • readonly segments: number — Segments SUBMITTED last frame — retained, immediate and particle alike. What the game asked for, counted whether or not a backend was there to draw it.
  • readonly shapes: number — Live retained shapes.
  • readonly fragments: number — Pieces currently flying from a shatter(). Watch it spike and fall back to zero.
  • readonly particles: number — Live particles from emit.
  • setRng(rng: () => number): void — Use a seeded generator for bursts and shatters, so a replay matches.
  • readonly viewRect: { x: number; y: number; w: number; h: number } — The view rectangle the layer last drew with, in world units.

Vector2dOptions

interface Vector2dOptions

Manifest options.

  • glow?: number — A multiplier over every glow in the layer. Default 1.
  • glowSize?: number — How far the halo reaches, in logical pixels. Default 16.
  • enabled?: boolean — Whether the layer draws. Default true.

VecTrailOptions

interface VecTrailOptions

Tuning for game.vector.trail().

  • length?: number — Samples kept. Default 40. The oldest is the tail.
  • minDist?: number — Minimum world distance between samples. Default 4. Below it a move folds into the head.
  • maxDist?: number — The longest move one sample may span. 0 (the default) turns the split off.
  • life?: number — Seconds a sample survives. 0 (the default) makes the trail length-bound only.
  • width?: number — Stroke width at the head. Default 5.
  • tailWidth?: number — Stroke width at the tail. Default 0.
  • alpha?: number — Alpha at the head. Default 1.
  • tailAlpha?: number — Alpha at the tail. Default 0.
  • color?: string — Colour at the head. Default white.
  • tailColor?: string — The colour to grade toward at the tail — a colour change, not a fade.
  • colors?: VecColorSource — A colour source stepped along the ribbon. Beats color.
  • glow?: number — Halo intensity at the head; it fades out with the alpha. Default 0.7.
  • dwell?: number — Beam dwell 0..1. Default 0.
  • z?: number — Depth, as on any line.