Library exports
Constants
Texture
UV
Mesh
Target
Transformable (wip)
Shader
Blend
Sets the canvas to initialise the drawing context from.
You must call this or autoCanvas() before making use of any other function or method
setTargetCanvas(canvas: HTMLCanvasElement) -> Target
Creates a new canvas and appends it to the body.
Automatically resizes the canvas to fit the screen and transforms it to CSS-pixel space for you
Will call the function renderFn every frame, with the canvas's width/height in CSS-pixel units, as well as the seconds passed since the last frame in dt
You must call this or setTargetCanvas() before making use of any other function or method
autoCanvas(renderFn: (width: number, height: number, dt: number) => void) -> Target
Construct a new uninitialised texture object. See its section for more information.
Texture(width?: int, height?: int, layers?: int, format?: valueof Formats, options?: TextureOptions) -> Texture
Construct a new mesh. See its section for more information.
Mesh() -> Mesh
Construct a new target from a size or texture. See its section for more information.
Target(width?: int, height?: int, format?: valueof Formats, stencil?: boolean) -> Target
Target(texture: Texture, layer?: int, stencil?: boolean) -> Target
Construct a new shader from GLSL source code. See its section for more information.
Shader(glslSource: string, textureTypes: Array<NORM? | FLOAT? | UINT? | LAYERED?>, outputType: NORM? | FLOAT? | UINT?) -> Shader
Represent a uv (A uv is a location within a texture, think a crop rectangle for a texture). See its section for more information.
uv(x?: number, y?: number, w?: number, h?: number, layer?: number) -> UV
Create a blend function. See its section for more information.
Blend(srcMult: BlendMultiplier, combineOp: BlendCombine, dstMult: BlendMultiplier) -> Blend
Change the output canvas's color space. display-p3 allows slightly more vibrant colors than srgb, while sacrificing equally in color precision
colorSpace(space: 'srgb' | 'display-p3')
The underlying WebGL2 context powering the library
gl: WebGL2RenderingContext
_ = undefined
PI = 3.141592653589793
PI2 = PI*2
More constants are exposed as library exports, see the constants section
_ = undefined
PI = 3.141592653589793
PI2 = PI*2
Formats = {
// Normal 8-bit-per-channel formats
R, RG, RGB, RGBA,
// Varying bits shared between different channels
// Upload data by packing channels into 8/16/32 bit ints
RGB565, RGB5_A1, RGB10_A2,
RGBA4, R11F_G11F_B10F, RGB9_E5,
// Integer 8, 16 and 32 bit textures
// UINT or UINT|ARRAY must be used in the array passed to Shader's textureTypes parameter
R8, RG8, RGB8, RGBA8,
R16, RG16, RGB16, RGBA16,
R32, RG32, RGB32, RGBA32,
// half-precision float textures, upload as Uint16Array
R16F, RG16F, RGB16F, RGBA16F,
// half-precision float textures that allow you
// to upload data via Float32Arrays
R16F_32F, RG16F_32F, RGB16F_32F, RGBA16F_32F,
// single-precision float textures
R32F, RG32F, RGB32F, RGBA32F
}
// Constants used by third argument to (Target).draw()
R, G, B, A, RGB = R|G|B, RGBA = R|G|B|A
IF_SET, IF_UNSET, DONT_DRAW
UNSET, SET, FLIP
DITHERING
// Constants used by BlendMultiplier
ZERO
RGB_ONE, A_ONE
ONE = RGB_ONE|A_ONE
RGB_SRC, A_SRC, SRC = RGB_SRC|A_SRC
RGB_ONE_MINUS_SRC, A_ONE_MINUS_SRC
ONE_MINUS_SRC = RGB_ONE_MINUS_SRC|A_ONE_MINUS_SRC
RGB_SRC_ALPHA, SRC_ALPHA = RGB_SRC_ALPHA|A_SRC
RGB_ONE_MINUS_SRC_ALPHA
ONE_MINUS_SRC_ALPHA = RGB_ONE_MINUS_SRC_ALPHA|A_ONE_MINUS_SRC
A_DST, RGB_DST, DST = RGB_DST|A_DST
RGB_ONE_MINUS_DST, A_ONE_MINUS_DST
ONE_MINUS_DST = RGB_ONE_MINUS_DST|A_ONE_MINUS_DST
RGB_DST_ALPHA, DST_ALPHA = RGB_DST_ALPHA|A_DST
RGB_ONE_MINUS_DST_ALPHA
ONE_MINUS_DST_ALPHA = RGB_ONE_MINUS_DST_ALPHA|A_ONE_MINUS_DST
RGB_SRC_ALPHA_SATURATE,
SRC_ALPHA_SATURATE = RGB_SRC_ALPHA_SATURATE|A_ONE
// Constants used by BlendCombine
RGB_ADD, A_ADD, ADD = RGB_ADD|A_ADD
RGB_SUBTRACT, A_SUBTRACT, SUBTRACT = RGB_SUBTRACT|A_SUBTRACT
RGB_REVERSE_SUBTRACT, A_REVERSE_SUBTRACT
REVERSE_SUBTRACT = RGB_REVERSE_SUBTRACT|A_REVERSE_SUBTRACT
RGB_MIN, A_MIN, MIN = RGB_MIN|A_MIN
RGB_MAX, A_MAX, MAX = RGB_MAX|A_MAX
// Constants used by TextureOptions
UPSCALE_PIXELATED, DOWNSCALE_PIXELATED, DOWNSCALE_MIPMAP_NEAREST
PIXELATED = UPSCALE_PIXELATED|DOWNSCALE_PIXELATED|DOWNSCALE_MIPMAP_NEAREST
MIPMAPS
REPEAT_X, REPEAT_Y, REPEAT = REPEAT_X|REPEAT_Y
REPEAT_MIRRORED_X, REPEAT_MIRRORED_Y
REPEAT_MIRRORED = REPEAT_MIRRORED_X|REPEAT_MIRRORED_Y
// Constants used by Shader() input/output type
NORM, FLOAT, UINT, LAYERED
// Constants used by Geometry() type
TRIANGLE_STRIP, TRIANGLES, TRIANGLE_FAN, LINE_LOOP, LINE_STRIP, LINES, POINTS
Types related to Texture
type ImageSource = Blob | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | ImageData | ImageBitmap
type TextureOptions = REPEAT_X? | REPEAT_Y? | REPEAT? | REPEAT_MIRRORED_X? | REPEAT_MIRRORED_Y? | REPEAT_MIRRORED? | UPSCALE_PIXELATED? | DOWNSCALE_PIXELATED? | DOWNSCALE_MIPMAP_NEAREST? | PIXELATED? | MIPMAPS?
default format = Formats.RGBA
Construct a new uninitialised texture object. Do not use new.
If layer == 0, the texture will be a flat texture, with no concept of layers. All functions that accept a layer argument will ignore that argument.
Otherwise, the texture is a layered texture, which may hold different data on each layer.
width and height are not guaranteed to be supported for values > 4096. Likewise layers for values > 256.
Textures may use a lot of graphical memory. Consider reusing old textures instead of initialising new ones, and .delete() textures when you are done with them to avoid risks of GPU crashes
constructor(width?: int, height?: int, layers?: int, format?: valueof Formats, options?: TextureOptions)
Set the default options parameter for all future texture initialisations
Texture.setDefaultOptions(options?: TextureOptions)
Initialise the texture from a URL or Image source. The texture may not be ready immediately, but is loaded in the background. If you need to run a callback after the image has loaded, use .from(...)
.fromSrc(src: string | ImageSource, format?: valueof Formats, options?: TextureOptions) -> this
Initialise the texture from an Image source. If the image source cannot be resolved immediately, a promise is returned, If you do not want to handle the case of when a promise is returned, use .fromSrc(...)
.from(img: ImageSource, format?: valueof Formats, options?: TextureOptions) -> Promise<this> | this
Initialise the texture as empty, same as the constructor
.of(width?: int, height?: int, layers?: int, format?: valueof Formats, options?: TextureOptions) -> this
Get or set the texture options for this texture
get/set options: TextureOptions
Convert a sub-rectangle specified in pixel coordinates from the top-left corner into a UV (UV coordinates are (0,0)-(1,1) from the bottom left)
Note that this function is only useful once the image has been fully loaded, as the actual width and height of the texture are required to perform the conversion
.uv(x?: number, y?: number, width?: number, height?: number, layer?: number) -> UV
Write an Image source within a sub-rect of the texture.
Just like with .from(...), a promise is returned if the image source cannot be resolved immediately
.put(img: ImageSource, x?: int, y?: int, layer?: int) -> Promise<this> | this
Put some pixels at a sub-rectangle in the texture, from a TypedArray.
This method is only applicable for flat textures (i.e .layers == 0).
If specified, format must be compatible with the texture's format or the operation will fail
.putData(data: Uint8Array | Uint16Array | Uint32Array | Float32Array, x?: int, y?: int, width?: int, height?: int, format?: valueof Formats)
Put some pixels at a sub-rectangle in the texture, from a TypedArray.
This method is only applicable for layered textures (i.e .layers > 0).
If specified, format must be compatible with the texture's format or the operation will fail
.putDataLayers(data: Uint8Array | Uint16Array | Uint32Array | Float32Array, x?: int, y?: int, layer?: int, width?: int, height?: int, depth?: int, format?: valueof Formats)
Delete the texture. Its resources will be freed once any draw operations making use of the texture are completed.
The texture cannot be used, modified or re-initialised again
.delete()
The texture's width and height in pixels
readonly .width: int
readonly .height: int
The number of layers in a layered texture, or 0 if the texture is flat and not layered
readonly .layers: int
The texture's format, specified when the texture was last initialised
readonly .format: valueof Formats
The texture's options, specified when the texture was last initialised or from .setOptions(...)
readonly .options: TextureOptions
type UV = {x: number, y: number, w: number, h: number, l: number}
Create a uv. Do not use new.
A uv is a location within a texture, think a crop rectangle for a texture.
Arguments default to uv(0, 0, 1, 1, 0), i.e, a square from the bottom-left (0, 0) with size (1, 1) (= the whole texture) at layer 0 (the default layer for most textures)
constructor(x?: number, y?: number, w?: number, h?: number, layer?: number) -> UV
Create a new uv as a sub-rect of another, imagining (0, 0) to be the bottom left of the reference uv and (1, 1) to be the top right
.sub(x?: number, y?: number, w?: number, h?: number) -> UV
Construct a new mesh. Do not use new.
Unlike textures, meshes use entirely CPU memory, exactly 64 bytes per sprite they contain. That means a mesh with 1,000,000 sprites will take up approximately 64MB of CPU memory while it is being built, up until it is consumed (either with mesh.upload() or (Target).draw(mesh))
constructor()
These arguments are used by most of Mesh's methods
arguments SpriteParams = (uv?: UV, effect?: number, r?: number, g?: number, b?: number, a?: number)
Construct a mesh consisting of a single sprite, optionally from a rectangle or 3x2 matrix.
This is a convenience function that is also usually more performant than its longer form
Mesh.single(...SpriteParams) -> Mesh
Mesh.singleRect(x: number, y: number, w: number, h: number, ...SpriteParams) -> Mesh
Mesh.singleMat(a: number, b: number, c: number, d: number, e: number, f: number, ...SpriteParams) -> Mesh
Meshes are transformable. The methods .add(...), .addRect(...) and .addMat(...) transform their coordinates based on the current transformation.
For example, calling .scale(3, 3) followed by .addRect(0, 0, 10, 10, ...) will in effect add a sprite of width and height 30
Mesh implements Transformable
Add a sprite at coordinates (0,0) to (1,1) with the specified sprite properties
.add(...SpriteParams)
Add a sprite at coordinates (x, y) and size (w, h) (so that the top-right corner is at (x+w, y+h)) with the specified sprite properties
.addRect(x?: number, y?: number, width?: number, height?: number, ...SpriteParams)
Add a sprite from a matrix with the specified sprite properties.
Matrices don't have to be hard to get right. The resulting sprite will have its:
- Bottom-left corner at (e, f)
- Bottom-right corner at (a, b) from the bottom-left corner (i.e, it will be at (e+a, f+b))
- Top-left corner at (c, d) from the bottom-left corner (i.e, it will be at (e+c, f+d))
- Top-right corner will be at (c, d) from the bottom-right corner, which is also (a, b) from the top-left corner (i.e, it will be at (e+a+c, f+b+d))
The resulting sprite will always be at least a parallelogram (if not a square or rectangle)
.addMat(a?: number, b?: number, c?: number, d?: number, e?: number, f?: number, ...SpriteParams)
Upload the mesh to the GPU. The mesh will be deleted and a MeshUpload object will be returned.
MeshUpload objects:
- Are opaque and cannot be modified, only referenced
- Can be passed to (Target).draw(...) instead of the mesh
- Are not consumable, they can be drawn an infinite number of times
- Have a .size property much like Meshes
Once you no longer need a MeshUpload object, consider calling .delete() on it
.upload() -> MeshUpload
Delete the mesh object.
Since both (Target).draw(...) and .upload(...) delete the mesh for you, the only time you will need this method is if the mesh was never consumed and you would like to free its resources immediately for performance reasons.
Any future calls to .add/addRect/addMat(...) or .upload/export/import(...) will throw an error
.delete()
Export all of the sprite data from this mesh as a Float32Array of length 16 * mesh.size. The mesh is not consumed and can still be expanded or drawn
.export() -> Float32Array
Import all of the sprite data from a Float32Array whose length is a multiple of 16. The imported sprites are appended to the end of this mesh
.import(data: Float32Array)
The number of sprites currently in this mesh
readonly .size: int
Construct a new draw target. Do not use new.
Draw targets facilitate drawing to textures or other objects. The value returned by setTargetCanvas(...) is itself a target, which draws its commands to the supplied <canvas> element
constructor(width?: int, height?: int, format?: valueof Formats, stencil?: boolean)
constructor(texture: Texture, layer?: int, stencil?: boolean)
Targets are transformable. The method .draw(...) transform all of the mesh's coordinates based on the current transformation.
For example, calling .rotate(PI/2) followed by .draw(myMesh, ...) will in effect draw myMesh rotated 90 degrees (PI/2 radians)
With the default transformation, the bottom-left of the target's output is at coordinates (0, 0) and the top-right is at coordinates (1, 1)
Target implements Transformable
Draws a mesh to the target's output
If textures is not specified, the textures from the previous .draw(...) call (chronologically) will be used, as long as the shader is the same as the one that was used with that previous draw call (if it isn't, then you must re-specify textures).
If textures is not an array, then textures = [textures].
If the provided mesh has more sprites than maxSprites, then only the first maxSprites sprites of the mesh will be drawn
.draw(mesh: Mesh | MeshUpload | Float32Array, textures?: Texture | Array<Texture>, maxSprites?: int)
Set draw mask options for future draw calls on this target subview (See more about subviews)
The default value is R | G | B | A (or short, RGBA).
R, G, B and A enable drawing to the respective color channels.
IF_SET and IF_UNSET limit drawing to only where the stencil buffer is set or unset repectively.
NO_DRAW forces the stencil test to always fail, resulting in no colors drawn.
SET and UNSET will set/unset the stencil value wherever something is drawn.
FLIP is the same as SET | UNSET and sets the stencil value where it was unset and unsets where it was set.
Stencil operations run everywhere the mesh is drawn. If more than one sprite in the mesh overlap, the specified stencil operation will be executed more than once
DITHERING will try to enable dithering to create smoother gradients, especially for low precision formats. This is a hint and is not guaranteed to have an effect on all devices
get/set .mask: R? | G? | B? | A? | RGB? | RGBA? | IF_SET? | IF_UNSET? | SET? | UNSET? | FLIP? | NO_DRAW? | DITHERING?
Set the blend operation for all future draw calls on this target subview
(See more about subviews)
The default value is Blend(ONE, ADD, ONE_MINUS_SRC_ALPHA)
get/set .blend: Blend
Set the shader for all future draw calls on this target subview
(See more about subviews; more on shaders & uniforms)
get/set .shader: Shader
Change the texture output of the target. All future draw operations will draw to this texture (at the specified layer if the texture is layered)
This method is invalid on the main target (the one returned by setTargetCanvas/autoCanvas(...))
.setTexture(texture: Texture, layer?: int)
Resize the target to the specified size
If the target was configured to draw to a texture, it will no longer draw to that texture but to a new surface constructed from the arguments.
If called on the main target, its <canvas> element will be resized. Chrome supports Formats.RGBA and Formats.RGBA16F for the main target, while most other browsers only support Formats.RGBA for the main target.
.resize(width: int, height: int, format?: valueof Formats)
Copy a sub-rect of the target's data to a texture. If the target has a texture output, the supplied texture cannot be the same as the target's output texture
.copyTo(texture: Texture, textureX?: int, textureY?: int, textureLayer?: int, x?: int, y?: int, width?: int, height?: int)
Read pixels from a sub-rect into a TypedArray.
If arr is specified, it must be of the right type and length. If it is, its contents will be replaced and the array will be returned.
If arr isn't specified, it will be automatically created with the right type and length and then returned
.getData(x?: int, y?: int, width?: int, height?: int, arr?: TypedArray) -> Uint8Array | Uint16Array | Uint32Array | Float32Array
Clear the target's output to a plain color (transparent black by default).
Set any of the arguments to NaN to not clear that channel.
Also clears the stencil buffer
.clear(r?: number, g?: number, b?: number, a?: number)
Clear the target's output to a plain color (transparent black by default).
Set any of the arguments to NaN to not clear that channel.
Does not clear the stencil buffer
.clearColor(r?: number, g?: number, b?: number, a?: number)
Clears the stencil buffer to zero
.clearStencil()
Delete the target and its resources. Does not delete its texture output (if it had one)
.delete()
The target's output's width and height in pixels
readonly .width: int
readonly .height: int
The target's texture output, if any
readonly .texture: Texture | null
work in progress :(
Create a shader. Do not use new.
When this shader is in use, the main() GLSL function will be invoked for every drawn pixel. It must write a color value to color, or execute discard; to indicate that the pixel is to be abandoned and nothing is to be drawn. Do not put a version header or uniforms inside your shader source, they are added for you by the library
constructor(glslSource: string, textureTypes: Array<NORM? | FLOAT? | UINT? | LAYERED?>, outputType: NORM? | FLOAT? | UINT?) -> Shader
Example of a valid shader:
(this is the default shader)
Shader(`void main(){
// Texture 0 is a flat texture therefore texture() expects a vec2 coordinate
color = texture(tex0, uv.xy) * (1.0 - tint);
}`, [NORM], NORM)
Example of default shader but for layered RGBA8 (uint) textures:
(this is the default shader)
Shader(`void main(){
// Texture 0 is a flat texture therefore texture() expects a vec2 coordinate
color = vec4(texture(tex0, uv.xy))/255.0 * (1.0 - tint);
}`, [UINT | LAYERED], NORM)
Values exposed in the GLSL shader:
uniform vec4 u;
uniform float s, t;
uniform (sampler2D | usampler2D | sampler2DArray | usampler2DArray) tex0, tex1, ..., tex15;
in vec2 pos, xy; // automatically generated
in vec3 uv;
in float effect;
in vec4 tint;
out (vec4 | uvec4) color;
The types and precisions of tex0, tex1, tex2, ... are determined by the values passed to textureTypes
pos represents the coordinate inside the sprite, where (0, 0) is always the bottom-left of the sprite and (1, 1) is always the top-right
xy represents the mesh-space coordinates of the current pixel
uv corresponds to the uv value supplied to SpiteParams
effect corresponds to the effect value supplied to SpiteParams
tint corresponds to the r, g, b, a values supplied to SpiteParams
Create a blend mode. Do not use new.
When this blend mode is in use, when drawing color src onto a canvas where the current pixel's color is dst, the blend mode will be used to determine how the two values are combined to determine the new value to write: src * srcMult <combineOp> dst * dstMult
constructor(srcMult: BlendMultiplier, combineOp: BlendCombine, dstMult: BlendMultiplier) -> Blend
Example: Blend(ONE, ADD, ONE_MINUS_SRC_ALPHA) (the default blend mode),
yields the equation src * 1 + dst * (1 - src.a), which can be simplified to src + dst*(1-src.a).
This has the effect that where the drawn sprites have alpha 0, the RGBA values are added to the destination's values, and whn the drawn sprites have alpha 1, the RGBA values replace the destination's values.
See further down for all possible blend multipliers and combine
Below is a list of all possible values for BlendMultiplier
Values that start with RGB_ only apply to the r, g and b channels
Values that start with A_ only apply to the alpha channel
A RGB_ and A_ value can be ORed ( | ) together to create a new blend multiplier.
Other values can only be used on their own
ZERO // * 0
ONE // * 1
SRC // * src.rgba
ONE_MINUS_SRC // * (1 - src.rgba)
SRC_ALPHA // * src.a
ONE_MINUS_SRC_ALPHA // * (1 - src.a)
DST // * dst.rgba
ONE_MINUS_DST // * (1 - dst.rgba)
DST_ALPHA // * dst.a
ONE_MINUS_DST_ALPHA // * (1 - dst.a)
SRC_ALPHA_SATURATE //.rgb * min(src.a, 1-dst.a), .a * 1
RGB_ONE //.rgb * 1
RGB_SRC //.rgb * src.rgb
RGB_ONE_MINUS_SRC //.rgb * (1 - src.rgb)
RGB_SRC_ALPHA //.rgb * src.a
RGB_ONE_MINUS_SRC_ALPHA //.rgb * (1 - src.a)
RGB_DST //.rgb * dst.rgb
RGB_ONE_MINUS_DST //.rgb * (1 - dst.rgb)
RGB_DST_ALPHA //.rgb * dst.a
RGB_ONE_MINUS_DST_ALPHA //.rgb * (1 - dst.a)
RGB_SRC_ALPHA_SATURATE //.rgb * min(src.a, 1-dst.a)
A_ONE //.a * 1
A_SRC //.a * src.a
A_ONE_MINUS_SRC //.a * (1 - src.a)
A_DST //.a * dst.a
A_ONE_MINUS_DST //.a * (1 - dst.a)
Below is a list of all possible values for BlendCombine
Values that start with RGB_ only apply to the r, g and b channels
Values that start with A_ only apply to the alpha channel
A RGB_ and A_ value can be ORed ( | ) together to create a new blend combine.
Other values can only be used on their own
ADD // srcResult + dstResult
SUBTRACT // srcResult - dstResult
REVERSE_SUBTRACT // dstResult - srcResult
MIN // min(srcResult, dstResult)
MAX // max(srcResult, dstResult)
RGB_ADD // srcResult.rgb + dstResult.rgb
RGB_SUBTRACT // srcResult.rgb - dstResult.rgb
RGB_REVERSE_SUBTRACT // dstResult.rgb - srcResult.rgb
RGB_MIN // min(srcResult.rgb, dstResult.rgb)
RGB_MAX // max(srcResult.rgb, dstResult.rgb)
A_ADD // srcResult.a + dstResult.a
A_SUBTRACT // srcResult.a - dstResult.a
A_REVERSE_SUBTRACT // dstResult.a - srcResult.a
A_MIN // min(srcResult.a, dstResult.a)
A_MAX // max(srcResult.a, dstResult.a)