textmode.js
Version:
textmode.js is a lightweight creative coding library for creating real-time ASCII art on the web.
1,072 lines (1,071 loc) • 33.3 kB
TypeScript
import type { GLFramebuffer, GLShader, TextmodeFramebufferOptions, UniformValue } from '../../../rendering/webgl';
import type { TextmodeImage } from '../../loadables/TextmodeImage';
import type { TextmodeColor } from '../../TextmodeColor';
import type { TextmodeVideo } from '../../loadables';
/**
* Interface for rendering capabilities that will be mixed into Textmodifier
*/
export interface IRenderingMixin {
/**
* Set a custom shader for subsequent rendering operations.
* @param shader The custom shader to use
*
* @example
* ```javascript
* const t = textmode.create({ width: 800, height: 600 });
*
* let glitchShader;
*
* t.setup(async() => {
* glitchShader = await t.createFilterShader(`#version 300 es
* precision highp float;
* in vec2 v_uv;
* uniform float u_intensity;
* layout(location = 0) out vec4 o_character;
* layout(location = 1) out vec4 o_primaryColor;
* layout(location = 2) out vec4 o_secondaryColor;
*
* void main() {
* vec2 offset = vec2(sin(v_uv.y * 50.0) * u_intensity, 0.0);
* float pattern = fract(v_uv.x * 20.0 + offset.x);
* vec3 color = vec3(pattern, 1.0 - pattern, 0.5);
* o_character = vec4(pattern, 0.0, 0.0, 0.0);
* o_primaryColor = vec4(color, 1.0);
* o_secondaryColor = vec4(color * 0.5, 1.0);
* }
* `);
*
* t.draw(() => {
* t.shader(glitchShader);
* t.setUniform('u_intensity', Math.sin(t.frameCount * 0.1) * 0.02);
* t.rect(t.grid.cols, t.grid.rows);
* });
* ```
*/
shader(shader: GLShader): void;
/**
* Create a new framebuffer for offscreen rendering.
*
* The framebuffer uses the same MRT structure as the main rendering pipeline.
* By default it allocates 4 attachments (character + color data).
*
* @param options Configuration options for the framebuffer.
* @returns A new Framebuffer instance.
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* });
*
* // Create a framebuffer with 50x30 grid cells
* const fb = t.createFramebuffer({
* width: 50,
* height: 30
* });
*
* t.draw(() => {
* // Render to framebuffer
* fb.begin();
* t.background(255, 0, 0);
* t.charColor(255);
* t.char('A');
* t.rect(20, 10);
* fb.end();
*
* // Render framebuffer to main canvas
* t.background(0);
* t.rotateZ(t.frameCount * 2);
* t.image(fb);
* });
* ```
*/
createFramebuffer(options: TextmodeFramebufferOptions): GLFramebuffer;
/**
* Draw a TextmodeFramebuffer, TextmodeImage, or TextmodeVideo to the current render target.
*
* @param source The TextmodeFramebuffer or TextmodeSource to render
* @param width Width to potentially scale the content
* @param height Height to potentially scale the content
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* });
*
* const fb = t.createFramebuffer({width: 30, height: 20});
*
* t.draw(() => {
* // Draw something to the framebuffer
* fb.begin();
* t.clear();
* t.charColor(255, 0, 0);
* t.char('A');
* t.rect(20, 10);
* fb.end();
*
* // Clear main canvas and render framebuffer content
* t.background(0);
*
* // Render at original size
* t.image(fb);
*
* // Render scaled version
* // t.image(fb, 60, 40);
* });
* ```
*/
image(source: GLFramebuffer | TextmodeImage | TextmodeVideo, width?: number, height?: number): void;
/**
* Load an image and return a TextmodeImage that can be drawn with image().
*
* The loaded image can be rendered to the canvas using the {@link image} method.
* This function returns a Promise that resolves when the image has loaded.
*
* @param src URL or existing HTMLImageElement
* @returns A Promise that resolves to a TextmodeImage object
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* });
*
* let img;
*
* t.setup(async () => {
* img = await t.loadImage('https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800&q=80');
* img.characters(" .:-=+*#%@");
* });
*
* t.draw(() => {
* t.background(0);
*
* // Draw the loaded image
* t.image(img);
* });
* ```
*/
loadImage(src: string | HTMLImageElement): Promise<TextmodeImage>;
/**
* Load a video and return a TextmodeVideo that can be drawn with image().
* @param src URL or existing HTMLVideoElement
* @param options Optional configuration for preloading behavior. Provide `frameRate` to preload frames, `onProgress` to observe preload progress, `onComplete` to know when preloading finished, and `onError` to catch preload failures.
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* });
*
* let video;
*
* t.setup(async () => {
* video = await t.loadVideo('https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4');
* // Start playback and enable looping so the video keeps playing
* video.play();
* video.loop();
*
* video.characters(" .:-=+*#%@");
* });
*
* t.draw(() => {
* t.background(0);
*
* // Draw the loaded video
* t.image(video);
* });
* ```
*/
loadVideo(src: string | HTMLVideoElement): Promise<TextmodeVideo>;
/**
* Set a uniform value for the current custom shader.
* @param name The name of the uniform variable
* @param value The value to set
*
* @example
* ```javascript
* const t = textmode.create({ width: 800, height: 600 });
*
* let pulseShader;
*
* t.setup(async () => {
* pulseShader = await t.createFilterShader(`#version 300 es
* precision highp float;
* in vec2 v_uv;
* uniform float u_time;
* layout(location = 0) out vec4 o_character;
* layout(location = 1) out vec4 o_primaryColor;
* layout(location = 2) out vec4 o_secondaryColor;
*
* void main() {
* float pulse = 0.5 + 0.5 * sin(u_time + length(v_uv - 0.5) * 8.0);
* vec3 color = vec3(pulse * 0.3, pulse * 0.8, pulse);
* o_character = vec4(pulse, 0.0, 0.0, 0.0);
* o_primaryColor = vec4(color, 1.0);
* o_secondaryColor = vec4(color * 0.3, 1.0);
* }
* `);
* });
*
* t.draw(() => {
* t.shader(pulseShader);
* t.setUniform('u_time', t.frameCount * 0.005);
* t.rect(t.grid.cols, t.grid.rows);
* });
* ```
*/
setUniform(name: string, value: UniformValue): void;
/**
* Set multiple uniform values for the current custom shader.
* @param uniforms Object containing uniform name-value pairs
*
* @example
* ```javascript
* const t = textmode.create({ width: 800, height: 600 });
*
* let rippleShader;
*
* t.setup(async() => {
* rippleShader = await t.createFilterShader(`#version 300 es
* precision highp float;
* in vec2 v_uv;
* uniform float u_time;
* uniform vec2 u_center;
* layout(location = 0) out vec4 o_character;
* layout(location = 1) out vec4 o_primaryColor;
* layout(location = 2) out vec4 o_secondaryColor;
*
* void main() {
* float dist = length(v_uv - u_center);
* float wave = sin(dist * 20.0 - u_time * 2.0) * 0.5 + 0.5;
* vec3 color = mix(vec3(0.2, 0.4, 0.8), vec3(0.9, 0.6, 0.3), wave);
* o_character = vec4(wave, 0.0, 0.0, 0.0);
* o_primaryColor = vec4(color, 1.0);
* o_secondaryColor = vec4(color * 0.4, 1.0);
* }
* `);
* });
*
* t.draw(() => {
* t.shader(rippleShader);
* t.setUniforms({
* u_time: t.frameCount * 0.0005,
* u_center: [0.5, 0.5]
* });
* t.rect(t.grid.cols, t.grid.rows);
* });
* ```
*/
setUniforms(uniforms: Record<string, UniformValue>): void;
/**
* Create a custom filter shader from fragment shader source code or a file path.
* The fragment shader automatically receives the standard vertex shader inputs
* and must output to the 3 MRT attachments (character/transform, primary color, secondary color).
* @param fragmentSource The fragment shader source code or a file path (e.g., './shader.frag')
* @returns A Promise that resolves to a compiled shader ready for use with {@link shader}
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* })
*
* let waveShader;
*
* t.setup(async () => {
* // Load shader from file
* waveShader = await t.createFilterShader('./shader.frag');
*
* // Or create from inline source
* // waveShader = await t.createFilterShader(`#version 300 es
* // precision highp float;
* //
* // in vec2 v_uv;
* // in vec3 v_character;
* // in vec4 v_primaryColor;
* // in vec4 v_secondaryColor;
* //
* // uniform float u_time;
* //
* // layout(location = 0) out vec4 o_character;
* // layout(location = 1) out vec4 o_primaryColor;
* // layout(location = 2) out vec4 o_secondaryColor;
* //
* // void main() {
* // // Shader code here
* // }
* // `);
* });
*
* t.draw(() => {
* if (waveShader) {
* t.shader(waveShader);
* t.setUniform('u_time', t.frameCount * 0.003);
* t.rect(t.grid.cols, t.grid.rows);
* }
* });
* ```
*/
createFilterShader(fragmentSource: string): Promise<GLShader>;
/**
* Sets the rotation angles for subsequent shape rendering operations.
*
* All geometries rotate around the center of the shape.
*
* @param degreesX The rotation angle in degrees around the X-axis (optional, defaults to 0)
* @param degreesY The rotation angle in degrees around the Y-axis (optional, defaults to 0)
* @param degreesZ The rotation angle in degrees around the Z-axis (optional, defaults to 0)
*
* @example
* ```javascript
* const t = textmode.create({ width: 800, height: 600 });
*
* t.draw(() => {
* t.background(0);
*
* // Draw three rectangles rotating in 3D space with different axes
* for (let i = 0; i < 3; i++) {
* t.push();
* t.translate(i * 15 - 15, 0, 0);
*
* const angle = t.frameCount * (1.5 + i * 0.5);
* // Each shape rotates around different combinations of axes
* t.rotate(angle * 0.7, angle * 0.5, angle);
*
* t.char(['T', 'X', 'T'][i]);
* t.charColor(100 + i * 60, 200 - i * 40, 255);
* t.rect(10, 10);
* t.pop();
* }
* });
* ```
*/
rotate(degreesX?: number, degreesY?: number, degreesZ?: number): void;
/**
* Sets the X-axis rotation angle for subsequent shape rendering operations.
*
* All geometries rotate around the center of the shape.
*
* @param degrees The rotation angle in degrees around the X-axis
*
* @example
* ```javascript
* const t = textmode.create({ width: 800, height: 600 });
*
* t.draw(() => {
* t.background(0);
* t.char('A');
* t.charColor(255, 150, 100);
* t.rotateX(t.frameCount * 2); // Flip forward/backward
* t.rect(12, 12);
* });
* ```
*/
rotateX(degrees: number): void;
/**
* Sets the Y-axis rotation angle for subsequent shape rendering operations.
*
* All geometries rotate around the center of the shape.
*
* @param degrees The rotation angle in degrees around the Y-axis
*
* @example
* ```javascript
* const t = textmode.create({ width: 800, height: 600 });
*
* t.draw(() => {
* t.background(0);
* t.char('B');
* t.charColor(100, 255, 200);
* t.rotateY(t.frameCount * 2); // Spin left/right
* t.rect(12, 12);
* });
* ```
*/
rotateY(degrees: number): void;
/**
* Sets the Z-axis rotation angle for subsequent shape rendering operations.
*
* All geometries rotate around the center of the shape.
*
* @param degrees The rotation angle in degrees around the Z-axis
*
* @example
* ```javascript
* const t = textmode.create({ width: 800, height: 600 });
*
* t.draw(() => {
* t.background(0);
* t.char('C');
* t.charColor(255, 220, 100);
* t.rotateZ(t.frameCount * 2); // Spin clockwise
* t.rect(12, 12);
* });
* ```
*/
rotateZ(degrees: number): void;
/**
* Sets the translation offsets for subsequent shape rendering operations.
*
* All geometries are displaced by the specified amounts. Similar to p5.js translate().
*
* @param x Translation along the X-axis in pixels (optional, defaults to 0)
* @param y Translation along the Y-axis in pixels (optional, defaults to 0)
* @param z Translation along the Z-axis in pixels (optional, defaults to 0)
*
* @example
* ```javascript
* const t = textmode.create({ width: 800, height: 600 });
*
* t.draw(() => {
* t.background(0);
*
* // Draw a grid of shapes with different translations
* for (let i = 0; i < 3; i++) {
* t.push();
* t.translate(i * 12 - 12, Math.sin(t.frameCount * 0.05 + i) * 3);
* t.char('A');
* t.charColor(100 + i * 70, 150, 255 - i * 50);
* t.rect(8, 8);
* t.pop();
* }
* });
* ```
*/
translate(x?: number, y?: number, z?: number): void;
/**
* Sets the X-axis translation offset for subsequent shape rendering operations.
*
* @param pixels The translation offset in pixels along the X-axis
*
* @example
* ```javascript
* const t = textmode.create({ width: 800, height: 600 });
*
* t.draw(() => {
* t.background(0);
* t.char('→');
* t.charColor(255, 180, 100);
* t.translateX(Math.sin(t.frameCount * 0.05) * 15); // Slide left/right
* t.rect(10, 10);
* });
* ```
*/
translateX(pixels: number): void;
/**
* Sets the Y-axis translation offset for subsequent shape rendering operations.
*
* @param pixels The translation offset in pixels along the Y-axis
*
* @example
* ```javascript
* const t = textmode.create({ width: 800, height: 600 });
*
* t.draw(() => {
* t.background(0);
* t.char('↓');
* t.charColor(100, 255, 180);
* t.translateY(Math.sin(t.frameCount * 0.05) * 10); // Bounce up/down
* t.rect(10, 10);
* });
* ```
*/
translateY(pixels: number): void;
/**
* Sets the Z-axis translation offset for subsequent shape rendering operations.
*
* @param pixels The translation offset in pixels along the Z-axis
*
* @example
* ```javascript
* const t = textmode.create({ width: 800, height: 600 });
*
* t.draw(() => {
* t.background(0);
* t.char('O');
* t.charColor(180, 120, 255);
* t.translateZ(Math.sin(t.frameCount * 0.05) * 20); // Pulse in/out
* t.rect(12, 12);
* });
* ```
*/
translateZ(pixels: number): void;
/**
* Save the current rendering state to the state stack.
* Use with {@link pop} to isolate style changes within a block.
*
* @example
* ```javascript
* const t = textmode.create({ width: 800, height: 600 });
*
* t.draw(() => {
* t.background(0);
*
* // Draw three rotating shapes with isolated transformations and colors
* for (let i = 0; i < 3; i++) {
* t.push(); // Save state
* t.translate(i * 12 - 12, 0);
* t.rotateZ(t.frameCount * (1 + i * 0.5));
* t.charColor(100 + i * 70, 255 - i * 50, 150);
* t.char(['*', '@', '#'][i]);
* t.rect(8, 8);
* t.pop(); // Restore state - next iteration starts fresh
* }
* });
* ```
*/
push(): void;
/**
* Restore the most recently saved rendering state from the state stack.
* Use with {@link push} to isolate style changes within a block.
*
* @example
* ```javascript
* const t = textmode.create({ width: 800, height: 600 });
*
* t.draw(() => {
* t.background(0);
*
* // Draw three rotating shapes with isolated transformations and colors
* for (let i = 0; i < 3; i++) {
* t.push(); // Save state
* t.translate(i * 12 - 12, 0);
* t.rotateZ(t.frameCount * (1 + i * 0.5));
* t.charColor(100 + i * 70, 255 - i * 50, 150);
* t.char(['*', '@', '#'][i]);
* t.rect(8, 8);
* t.pop(); // Restore state - next iteration starts fresh
* }
* });
* ```
*/
pop(): void;
/**
* Create a reusable color object compatible with textmode drawing APIs.
*
* Accepts grayscale, RGB, RGBA, and hex string values as arguments. Returned
* {@link TextmodeColor} instances can be passed to {@link background},
* {@link char}, {@link charColor}, {@link cellColor}, and more.
*
* @param value Grayscale value, hex string, single character, or an existing color
* @param g Optional green component, or `value` when using grayscale form
* @param b Optional blue component, or `value` when using grayscale form
* @param a Optional alpha component when using RGB form
*
* Example usage of the {@link color} helper.
*
* @example
* ```javascript
* const t = textmode.create({ width: 800, height: 600 });
*
* // Grayscale (0 = black, 255 = white)
* const gray = t.color(128);
*
* // RGB
* const hotPink = t.color(255, 105, 180);
*
* // RGBA (alpha 0-255)
* const semiTransparentRed = t.color(255, 0, 0, 128);
*
* // Hex string
* const dusk = t.color('#203040');
*
* t.draw(() => {
* // Using colors with other drawing APIs
* t.background(gray);
* t.charColor(hotPink);
* t.char('A');
* t.rect(5, 5);
*
* t.translate(5, 0);
* t.cellColor(dusk);
* t.char('*');
* t.rect(5, 5);
*
* t.translate(5, 0);
* t.charColor("#FF00FF");
* t.char("B");
* t.rect(5, 5);
* });
* ```
*/
color(value: number | string | TextmodeColor, g?: number, b?: number, a?: number): TextmodeColor;
/**
* Draw a rectangle with the current settings.
* Position is controlled via {@link translate}, {@link push}, and {@link pop}.
* @param width Width of the rectangle
* @param height Height of the rectangle
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* })
*
* t.draw(() => {
* // Set the background color to black
* t.background(0);
*
* // Position and draw a filled rectangle
* t.char('A');
* t.charColor(255, 255, 255); // White
* t.rotateZ(t.frameCount * 2);
* t.rect(16, 16);
* });
* ```
*/
rect(width?: number, height?: number): void;
/**
* Draw a 1x1 rectangle with the current settings.
*
* @example
* ```javascript
* const t = textmode.create({ width: 800, height: 600 });
*
* t.draw(() => {
* t.background(0);
*
* const angle = t.frameCount * 0.06;
* const radius = Math.min(t.grid.cols, t.grid.rows) * 0.35;
*
* // Draw a short trail of points behind the leading point
* for (let i = 0; i < 10; i++) {
* const a = angle - i * 0.18;
* const r = radius * (1 - i * 0.08);
* const x = Math.round(Math.cos(a) * r);
* const y = Math.round(Math.sin(a) * r);
*
* // Color and brightness fade across the trail
* const brightness = Math.max(40, 255 - i * 20);
* const blue = Math.max(60, 255 - i * 25);
* const green = 120 + i * 8;
*
* t.push();
* t.translate(x, y);
* t.char('*');
* t.charColor(brightness, green, blue);
* t.point();
*
* t.pop();
* }
*
* // Leading point drawn with highest brightness
* t.char('@');
* t.charColor(255, 255, 160);
* t.translate(Math.round(Math.cos(angle) * radius), Math.round(Math.sin(angle) * radius));
* t.point();
* });
* ```
*/
point(): void;
/**
* Draw a line from point (x1, y1) to point (x2, y2) with the settings.
* @param x1 X-coordinate of the line start point
* @param y1 Y-coordinate of the line start point
* @param x2 X-coordinate of the line end point
* @param y2 Y-coordinate of the line end point
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* })
*
* t.draw(() => {
* t.background(0);
*
* t.char('*');
* t.charColor(255, 100, 255); // Magenta
* t.lineWeight(2);
*
* const halfWidth = 5;
* const halfHeight = 7.5;
*
* t.push();
* t.rotateZ(t.frameCount * 2);
* t.line(-halfWidth, halfHeight, halfWidth, -halfHeight);
* t.pop();
* });
* ```
*/
line(x1: number, y1: number, x2: number, y2: number): void;
/**
* Set the background color of the layer currently drawing to.
*
* Used to clear the layer to a specific color at the start of its drawing cycle.
*
* @param value A {@link TextmodeColor}, hex string, grayscale value, or single RGB channel
* @param g Optional green component when providing RGB channels or alpha when used with grayscale
* @param b Optional blue component when providing RGB channels
* @param a Optional alpha component (0-255)
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* });
*
* const midnight = t.color('#0b1d3a');
*
* t.draw(() => {
* // Set the background using a reusable color
* t.background(midnight);
*
* // Or inline RGB(A) notation
* //t.background(32, 48, 64);
*
* // Or hex string
* //t.background('#203040');
*
* t.char('M');
* t.rotateZ(t.frameCount * 2);
* t.rect(12, 12);
* });
* ```
*/
background(value: number | string | TextmodeColor, g?: number, b?: number, a?: number): void;
/**
* Clear the layer currently drawing to.
*
* Used to clear the layer at the start of its drawing cycle.
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* })
*
* t.draw(() => {
* // Clear the canvas
* t.clear();
* });
* ```
*/
clear(): void;
/**
* Update the line weight (thickness) for subsequent {@link line} and {@link bezierCurve} calls.
* @param weight The line weight (thickness) to set.
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* })
*
* t.draw(() => {
* t.background('#050810');
*
* // Animate the weight so every line breathes differently
* const layers = 6;
* const halfCols = t.grid.cols / 2;
* const spacing = 4;
*
* for (let i = 0; i < layers; i++) {
* const phase = t.frameCount * 0.03 + i * 0.8;
* const pulse = 0.75 + 3.25 * (0.5 + 0.5 * Math.sin(phase));
* const wobble = Math.sin(phase * 1.6) * 4;
* const centeredRow = (i - (layers - 1) / 2) * spacing;
*
* t.lineWeight(Math.round(pulse));
* t.charColor(160 + i * 12, 200 - i * 8, 255);
* t.char('-');
* t.line(
* -halfCols + 2,
* centeredRow + wobble,
* halfCols - 2,
* centeredRow - wobble,
* );
* }
* });
* ```
*/
lineWeight(weight: number): void;
/**
* Draw a smooth cubic bezier curve between two points with two control points.
* The curve thickness is controlled by the current {@link lineWeight} setting.
* @param x1 Start point X coordinate
* @param y1 Start point Y coordinate
* @param cp1x First control point X coordinate
* @param cp1y First control point Y coordinate
* @param cp2x Second control point X coordinate
* @param cp2y Second control point Y coordinate
* @param x2 End point X coordinate
* @param y2 End point Y coordinate
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* })
*
* t.draw(() => {
* t.background(0);
* t.translate(-t.grid.cols / 2, -t.grid.rows / 2);
*
* // Draw a smooth S-curve
* t.char('*');
* t.charColor(255, 100, 255); // Magenta
* t.lineWeight(2);
*
* // Rotate the curve around its geometric center
* // The bezier's control points: (5,20), (15,5), (25,35), (35,20)
* // Center = average of points; translate to center then draw with local coordinates
* const cx = (5 + 15 + 25 + 35) / 4;
* const cy = (20 + 5 + 35 + 20) / 4;
*
* t.translate(cx, cy);
* t.rotateZ(t.frameCount * 2);
* t.bezierCurve(5 - cx, 20 - cy, 15 - cx, 5 - cy, 25 - cx, 35 - cy, 35 - cx, 20 - cy);
* });
* ```
*/
bezierCurve(x1: number, y1: number, cp1x: number, cp1y: number, cp2x: number, cp2y: number, x2: number, y2: number): void;
/**
* Set the character to be used for subsequent rendering operations.
* Accepts a single character string.
*
* @param character The character to set for rendering
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* })
*
* t.draw(() => {
* t.background(0);
* t.char('A');
* t.rect(10, 10);
*
* t.char(";");
* t.translate(15, 0);
* t.rect(10, 10);
* });
* ```
*/
char(character: string): void;
/**
* Set the character color for subsequent rendering operations.
* Accepts channel values, hex strings, or a {@link TextmodeColor} instance.
* @param value Color object, hex string, or grayscale value (0-255)
* @param g Optional green component when providing RGB values or alpha when using grayscale form
* @param b Optional blue component when providing RGB values
* @param a Optional alpha component (0-255)
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* })
*
* const hotPink = t.color(255, 105, 180);
*
* t.draw(() => {
* t.background(0);
* t.char('A');
* t.charColor(hotPink);
* t.rect(10, 10);
* });
* ```
*/
charColor(value: number | string | TextmodeColor, g?: number, b?: number, a?: number): void;
/**
* Set the cell background color for subsequent rendering operations.
* Accepts channel values, hex strings, or a {@link TextmodeColor} instance.
* @param value Color object, hex string, or grayscale value (0-255)
* @param g Optional green component when providing RGB values or alpha when using grayscale form
* @param b Optional blue component when providing RGB values
* @param a Optional alpha component (0-255)
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* })
*
* const dusk = t.color('#203040');
*
* t.draw(() => {
* t.background(0);
* t.cellColor(dusk);
* t.char('A');
* t.rotateZ(t.frameCount * 2);
* t.rect(10, 10);
* });
* ```
*/
cellColor(value: number | string | TextmodeColor, g?: number, b?: number, a?: number): void;
/**
* Toggle horizontal flipping for subsequent character rendering.
* @param toggle Whether to flip horizontally
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* })
*
* t.draw(() => {
* t.background(0);
* t.flipX(true);
* t.char('A');
* t.rect(5, 5);
* });
* ```
*/
flipX(toggle: boolean): void;
/**
* Toggle vertical flipping for subsequent character rendering.
* @param toggle Whether to flip vertically
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* })
*
* t.draw(() => {
* t.background(0);
* t.flipY(true);
* t.char('A');
* t.rect(5, 5);
* });
* ```
*/
flipY(toggle: boolean): void;
/**
* Set the character rotation angle for subsequent character rendering.
* @param degrees The rotation angle in degrees
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* })
*
* t.draw(() => {
* t.background(0);
* t.char('A');
* t.charRotation(90); // Rotate character 90 degrees
* t.rotateZ(t.frameCount * 2);
* t.rect(10, 10);
* });
* ```
*/
charRotation(degrees: number): void;
/**
* Toggle color inversion for subsequent character rendering.
* @param toggle Whether to invert colors
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* })
*
* t.draw(() => {
* t.background(0);
* t.invert(true);
* t.char('A');
* t.rotateZ(t.frameCount * 2);
* t.rect(5, 5);
* });
* ```
*/
invert(toggle: boolean): void;
/**
* Draw an ellipse with the current settings.
* Position is controlled via {@link translate}, {@link push}, and {@link pop}.
* @param width Width of the ellipse
* @param height Height of the ellipse
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* })
*
* t.draw(() => {
* t.background(0);
* t.char('O');
* t.rotateZ(t.frameCount * 2);
* t.ellipse(10, 8);
* });
* ```
*/
ellipse(width: number, height: number): void;
/**
* Draw a triangle with the current settings.
* @param x1 X-coordinate of the first vertex
* @param y1 Y-coordinate of the first vertex
* @param x2 X-coordinate of the second vertex
* @param y2 Y-coordinate of the second vertex
* @param x3 X-coordinate of the third vertex
* @param y3 Y-coordinate of the third vertex
*
* @example
* ```javascript
* const t = textmode.create({ width: 800, height: 600 });
*
* t.draw(() => {
* t.background(0);
* t.char('*');
* t.charColor(255, 100, 150);
*
* const angle = t.frameCount * 0.02;
* const size = 15;
* t.triangle(
* Math.cos(angle) * size, Math.sin(angle) * size,
* Math.cos(angle + 2.09) * size, Math.sin(angle + 2.09) * size,
* Math.cos(angle + 4.19) * size, Math.sin(angle + 4.19) * size
* );
* });
* ```
*/
triangle(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number): void;
/**
* Draw an arc with the current settings.
* Position is controlled via {@link translate}, {@link push}, and {@link pop}.
* @param width Width of the arc
* @param height Height of the arc
* @param startAngle Starting angle in degrees
* @param endAngle Ending angle in degrees
*
* @example
* ```javascript
* const t = textmode.create({
* width: 800,
* height: 600,
* })
*
* t.draw(() => {
* t.background(0);
* t.rotateZ(t.frameCount);
* t.char('A');
* t.arc(10, 10, 0, 90);
* });
* ```
*/
arc(width: number, height: number, startAngle: number, endAngle: number): void;
}