Vectra is a lightweight 2D rendering and linear algebra library for the browser, built on top of the HTML Canvas API. It provides geometric primitives (Vector2, Rect, Circle), affine transformations (Matrix3, Transform), color manipulation (Color), user input handling (InputManager), and a renderer (CanvasRenderer) that abstracts the native Canvas context, allowing you to draw shapes, apply transformations, and manage scenes in a structured way.
import {
Vector2,
Rect,
Circle,
Color,
Matrix3,
Transform,
InputManager,
CanvasRenderer,
} from "https://cdn.jsdelivr.net/gh/caetanoag/Vectra/lib/index.js";If you clone the repository, you can import from the lib/ folder — you only need that directory:
import {
Vector2,
Rect,
Circle,
Color,
Matrix3,
Transform,
InputManager,
CanvasRenderer,
} from "./lib/index.js";Vectra ships as a plain ES module — there are no runtime dependencies and no build step required.
git clone https://github.com/caetanoag/Vectra.git
cd VectraThat clones the whole repository, but you only need the lib/ directory. You can delete everything else. From the parent folder, first verify what will be removed:
cd ..
# Prints everything that will be deleted; make sure it looks correct
find ./Vectra/ -mindepth 1 -path "./Vectra/lib" -prune -o -printThen delete every file except lib/:
# Deletes everything in Vectra/, except the lib directory
find ./Vectra/ -mindepth 1 -path "./Vectra/lib" -prune -o -exec rm -rf {} +Now import from ./Vectra/lib/index.js (or copy the lib/ folder into your project).
Developers: the steps above apply to consumers only — do not delete
src/orpackage.json. From the repository root, runyarn install && yarn buildto regenerate thelib/folder (JavaScript files and TypeScript declarations).
import {
CanvasRenderer,
Circle,
Color,
Rect,
Vector2,
} from "https://cdn.jsdelivr.net/gh/caetanoag/Vectra/lib/index.js";
const canvas = document.getElementById("canvas");
const renderer = new CanvasRenderer(canvas);
renderer.setSize(800, 600);
// Background
renderer.clear();
renderer.fillRect(new Rect(0, 0, 800, 600), Color.fromHex("#2c3e50"));
// Rectangle
const rect = new Rect(50, 50, 200, 100);
renderer.fillRect(rect, Color.fromRgb(52, 152, 219));
renderer.strokeRect(rect, Color.white(), 2);
// Circle
const circle = new Circle(new Vector2(400, 200), 80);
renderer.fillCircle(circle, Color.fromRgb(231, 76, 60));Immutable 2D vector — all operations return new instances.
new Vector2(x, y)—xandymust be finitestatic zero/static one/static right/static up—Vector2constantsadd(v): Vector2/subtract(v): Vector2translate(dx, dy): Vector2– adds a displacementscale(factor): Vector2– multiplies by a scalarnegate(): Vector2truncate(): Vector2clamp(min, max): Vector2– clamps components to a boxclampLength(min, max): Vector2– clamps the magnitude (direction preserved)hadamar(v): Vector2– component-wise (Hadamard) productdot(v): number/cross(v): numberlerp(v, t): Vector2– linear interpolation,tin[0, 1]rotate(angle): Vector2– counter-clockwise rotation (radians)distanceTo(v): numberlength: number(getter) /lengthSq: number(getter) – squared length for fast comparisonsangle: number(getter) – direction in radiansgetAngle(v): number– angle from this vector to anotherangleTo(v): number– signed angle in[-π, π]normalized(): Vector2withLength(newLength): Vector2withX(newX): Vector2/withY(newY): Vector2equals(v, epsilon?): boolean– tolerance-based comparisonclone(): Vector2toString(): stringstatic fromAngle(radians): Vector2– unit vector from an anglestatic fromPolar(angle, length?): Vector2– from polar coordinates
Axis-aligned rectangle (AABB). Most methods mutate the instance and return this for chaining.
new Rect(x, y, width, height)— validates and normalizes (negative width/height are corrected automatically)setWidth(w): this/setHeight(h): thismoveTo(x, y): this/setPosition(v): this/setSize(v): thistranslate(dx, dy): this/resize(dx, dy): thisinflate(dx, dy): this– expands in all directions while keeping the center fixedscale(sx, sy): this– scales from the top-left cornerscaleFromCenter(sx, sy): this– scales keeping the center fixedround(): thiscontains(point: Vector2): booleancontainsBox(box: Rect): booleanintersects(box: Rect): booleanunion(box: Rect): Rect/intersection(box: Rect): Rect | undefinedclampPoint(point: Vector2): Vector2– clamps a point into the rectangledistanceToPoint(point: Vector2): number/distanceSquaredToPoint(point: Vector2): numberisEmpty(): booleanequals(box: Rect): booleanclone(): RectgetWidth(): number/getHeight(): numberleft, right, top, bottom– getters (numbers)position, center, size– getters (Vector2)area– getter (number)aspectRatio– getter (number)static fromCenter(center, size): Rect/static fromCorners(a, b): Rectstatic generateRandomInside(boundary, minWidth?, minHeight?): Rect
Circle defined by a center point and a finite, non-negative radius. center and radius are public; mutators return this for chaining.
new Circle(center: Vector2, radius)—radiusmust be finite and≥ 0setRadius(r): this/setCenter(v): thistranslate(dx, dy): this/scale(factor): thisarea– getter (πr²)circumference– getter (2πr)diameter– getter (2r)radiusSquared– getter (r²)boundingBox: Rect– getter, axis-aligned box that encloses the circlestring: string– getter, equivalent totoString()equationString: string– getter, Cartesian equation(x − h)² + (y − k)² = r²containsPoint(point: Vector2, epsilon?): boolean– inside or on the boundaryisPointOnCircumference(point: Vector2, epsilon?): booleandistanceTo(v: Vector2 | Circle): number– distance between centerspointAt(angle): Vector2– point on the circumference at an angle (radians)intersects(other: Circle, epsilon?): boolean– overlap or touchcontainsCircle(other: Circle, epsilon?): booleancontainsBox(box: Rect, epsilon?): booleanequals(other: Circle, epsilon?): booleanclone(): CirclewithCenter(v): Circle/withRadius(r): Circle– immutable variantstoString(): string
Immutable color with channels normalized to [0, 1].
new Color(r, g, b, a?)static fromHex(hex: string): Color– supports#RGB,#RGBA,#RRGGBB,#RRGGBBAAstatic fromRgb(r, g, b, a?): Color– channels in0-255static fromHsl(h, s, l): Color– hue in degrees, saturation/lightness in0-1hex: string–#RRGGBBor#RRGGBBAArgb: string– CSSrgb(...)rgba: string– CSSrgba(...)toArray: [number, number, number, number | undefined]brightness: number– approximate luminancehsl: { hue, saturation, lightness }– HSL representationlerp(other, t): ColorwithAlpha(alpha): Colordarken(amount): Color/lighten(amount): Color– in HSL spaceclone(): Colorequals(other, epsilon?): booleanstatic white() / black() / red() / green() / blue() / transparent(): Color
3x3 matrix for 2D affine transformations. Immutable — all operations return new instances. Row-major storage.
new Matrix3(m00, m01, m02, m10, m11, m12, m20, m21, m22)static identity(): Matrix3static translation(tx, ty): Matrix3static rotation(angle, center?): Matrix3– optionalcenter, defaults to the originstatic scaling(sx, sy?): Matrix3– optionalsy(uniform scaling if omitted)multiply(other): Matrix3translate(dx, dy): Matrix3/rotate(angle): Matrix3/scale(sx, sy?): Matrix3applyToVector(v): Vector2applyToDirection(v): Vector2– ignores translationtoCanvasTransform(): [a, b, c, d, e, f]– format used byCanvasRenderingContext2D.setTransforminvert(): Matrix3 | nullequals(other, epsilon?): booleanclone(): Matrix3toArray(): number[]toString(): string
Represents 2D position, rotation, and scale, with support for hierarchies via parent. Mutable — methods mutate the instance and return this.
new Transform(position?, rotation?, scale?)– defaults:(0,0),0,(1,1)setPosition(v): this/setRotation(angle): this/setScale(v): thistranslate(dx, dy): thisrotate(angle): this– adds to the current rotationscaleBy(sx, sy): this– multiplies the current scalelookAt(target): this– rotates to face a pointgetMatrix(): Matrix3– local matrix (order: scale → rotation → translation)parent: Transform | null(getter) /setParent(parent): voidgetWorldMatrix(): Matrix3– combines with the parent chain
Manages keyboard, mouse, and touch input. update() must be called once per frame to clear "pressed" states.
new InputManager(target: HTMLElement | Window)–targetreceives mouse/touch events; keyboard events are always attached towindowisKeyDown(key): booleanisKeyPressed(key): boolean– one-shot event, true only on the frame the key was pressedisMouseDown(button?): boolean–0left,1middle,2rightgetMousePosition(): Vector2– relative to the target elementisMouseOver(): booleanupdate(): void– must be called once per frame, before checking inputs
Wraps the HTML Canvas 2D context, providing a higher-level API for shapes, transforms, text, and state management.
new CanvasRenderer(canvas: HTMLCanvasElement)width, height– gettersboundingRect: Rect– getter forRect(0, 0, width, height)context: CanvasRenderingContext2D– getter for the raw context (use cautiously)setSize(width, height): voidclear(rect?): void– clears the whole canvas, or only the given region
Shapes
fillRect(rect, color): voidstrokeRect(rect, color, lineWidth?): voidfillCircle(circle: Circle, color): void– orfillCircle(center: Vector2, radius, color)strokeCircle(circle: Circle, color, lineWidth?): void– orstrokeCircle(center: Vector2, radius, color, lineWidth?)fillPolygon(points, color): voidstrokePolygon(points, color, lineWidth?): voiddrawLine(from, to, color, lineWidth?): void
Transforms
translate(dx, dy): void/rotate(angle): void/scale(sx, sy): void– apply directly to the canvas contextsave(): void/restore(): voidresetTransform(): void– resets to identityapplyMatrix(matrix: Matrix3): void– multiplies the current transform by aMatrix3applyTransform(transform: Transform): void– multiplies by aTransform's world matrixsetTransform(transform: Transform): void– sets the current transform from aTransform's world matrix
Text
fillText(text, position, color, options?): voidstrokeText(text, position, color, options?): voidmeasureText(text, options?): TextMetrics
Text options (TextOptions):
| Field | Default | Description |
|---|---|---|
fontFamily |
'sans-serif' |
Font family |
fontSize |
16 |
Size in pixels |
fontStyle |
'normal' |
'normal', 'italic', or 'oblique' |
fontWeight |
'normal' |
Font weight |
textAlign |
'start' |
Text alignment (CanvasTextAlign) |
textBaseline |
'alphabetic' |
Text baseline (CanvasTextBaseline) |
maxWidth |
— | Maximum rendering width |