Skip to content

textmode.js / loadables / TextmodeVideo

Class: TextmodeVideo

Represents a video element for textmode rendering via Textmodifier.loadVideo.

It can be drawn to the canvas via Textmodifier.image.

A video uploaded currently runs through an adjustable brightness-converter that converts the video frames into a textmode representation using characters. Those adjustable options are available via chainable methods on this class.

Example

javascript
const VIDEO_URL = 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4';
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let video;

function drawLabel(text, y) {
	t.push();
	t.translate(-Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(async () => {
	video = await t.loadVideo(VIDEO_URL);
	await video.play();
	video.loop();
	video.characters(' .:-=+*#%@');
});

t.draw(() => {
	t.background(0);
	if (!video) return;

	t.image(video);
	drawLabel('TextmodeVideo via loadVideo()', Math.floor(t.grid.rows / 2) - 2);
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

Extends

Accessors

currentTime

Get Signature

ts
get currentTime(): number;

Current playback time in seconds.

Returns

number


duration

Get Signature

ts
get duration(): number;

Total duration of the video in seconds.

Returns

number


height

Get Signature

ts
get height(): number;

Ideal height in grid cells.

Returns

number

Inherited from

TextmodeTexture.height


isPlaying

Get Signature

ts
get isPlaying(): boolean;

Whether the video is currently playing.

Returns

boolean


originalHeight

Get Signature

ts
get originalHeight(): number;

Original pixel height.

Returns

number

Inherited from

TextmodeTexture.originalHeight


originalWidth

Get Signature

ts
get originalWidth(): number;

Original pixel width.

Returns

number

Inherited from

TextmodeTexture.originalWidth


source

Get Signature

ts
get source(): HTMLCanvasElement | HTMLVideoElement;

The source element this texture captures from.

Returns

HTMLCanvasElement | HTMLVideoElement

Inherited from

TextmodeTexture.source


texture

Get Signature

ts
get texture(): WebGLTexture;

Return the WebGL texture currently backing this source.

Returns

WebGLTexture

Inherited from

TextmodeTexture.texture


videoElement

Get Signature

ts
get videoElement(): HTMLVideoElement;

The underlying HTML video element.

Returns

HTMLVideoElement


width

Get Signature

ts
get width(): number;

Ideal width in grid cells.

Returns

number

Inherited from

TextmodeTexture.width

Methods

_hasFrameOverrides()

ts
_hasFrameOverrides(): boolean;

Returns

boolean

Inherited from

TextmodeTexture._hasFrameOverrides


background()

ts
background(
   colorOrGray, 
   g?, 
   b?, 
   a?): this;

Defines the background color used for transparent pixels.

Parameters

ParameterTypeDescription
colorOrGraystring | number | TextmodeColorA grayscale value (0-255), hex string ('#RGB', '#RRGGBB', '#RRGGBBAA'), or TextmodeColor instance
g?numberOptional green component (0-255) if using RGB format, or alpha (0-255) when using grayscale form
b?numberOptional blue component (0-255) if using RGB format
a?numberOptional alpha component (0-255) if using RGBA format

Returns

this

This instance for chaining.

Example

javascript
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let plainSource;
let backgroundSource;

function createTransparentCanvas() {
	const canvas = document.createElement('canvas');
	canvas.width = 160;
	canvas.height = 160;

	const ctx = canvas.getContext('2d');
	if (!ctx) return canvas;

	ctx.clearRect(0, 0, canvas.width, canvas.height);
	ctx.fillStyle = '#ffffff';
	ctx.beginPath();
	ctx.arc(canvas.width / 2, canvas.height / 2, 48, 0, Math.PI * 2);
	ctx.fill();

	ctx.fillStyle = '#000000';
	ctx.fillRect(canvas.width / 2 - 10, canvas.height / 2 - 10, 20, 20);

	return canvas;
}

function drawLabel(text, x, y) {
	t.push();
	t.translate(x - Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(() => {
	const canvas = createTransparentCanvas();

	plainSource = t.createTexture(canvas);
	plainSource.characters(' .:-=+*#%@');

	backgroundSource = t.createTexture(canvas);
	backgroundSource.characters(' .:-=+*#%@');
	backgroundSource.background(255, 0, 0);
});

t.draw(() => {
	t.background(40);
	if (!plainSource || !backgroundSource) return;

	const size = Math.min(plainSource.width, plainSource.height) * 0.7;
	const offset = Math.floor(size * 0.7);

	t.push();
	t.translate(-offset, 0);
	t.image(plainSource, size, size);
	t.pop();

	t.push();
	t.translate(offset, 0);
	t.image(backgroundSource, size, size);
	t.pop();

	drawLabel('default transparent fallback', -offset, Math.floor(t.grid.rows / 2) - 2);
	drawLabel('background(255, 0, 0)', offset, Math.floor(t.grid.rows / 2) - 2);
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

Inherited from

TextmodeTexture.background


cellColor()

ts
cellColor(
   colorOrGray, 
   g?, 
   b?, 
   a?): this;

Defines the cell color when cellColorMode is 'fixed'.

Parameters

ParameterTypeDescription
colorOrGraystring | number | TextmodeColorA grayscale value (0-255), hex string ('#RGB', '#RRGGBB', '#RRGGBBAA'), or TextmodeColor instance
g?numberOptional green component (0-255) if using RGB format, or alpha (0-255) when using grayscale form
b?numberOptional blue component (0-255) if using RGB format
a?numberOptional alpha component (0-255) if using RGBA format

Returns

this

This instance for chaining.

Example

javascript
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let source;

function createGradientCanvas() {
	const canvas = document.createElement('canvas');
	canvas.width = 160;
	canvas.height = 160;

	const ctx = canvas.getContext('2d');
	if (!ctx) return canvas;

	const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
	gradient.addColorStop(0, '#050505');
	gradient.addColorStop(1, '#f5f5f5');
	ctx.fillStyle = gradient;
	ctx.fillRect(0, 0, canvas.width, canvas.height);

	return canvas;
}

function drawLabel(text, y) {
	t.push();
	t.translate(-Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(() => {
	source = t.createTexture(createGradientCanvas());
	source.characters(' .:-=+*#%@');
	source.charColorMode('fixed');
	source.charColor(255);
	source.cellColorMode('fixed');
});

t.draw(() => {
	t.background(0);
	if (!source) return;

	const blue = 32 + Math.round(64 * (1 + Math.sin(t.frameCount * 0.05)));
	source.cellColor('#000033');
	source.cellColor(0, 0, blue);
	t.image(source, source.width, source.height);
	drawLabel('cellColor(0, 0, blue)', Math.floor(t.grid.rows / 2) - 2);
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

Inherited from

TextmodeTexture.cellColor


cellColorMode()

ts
cellColorMode(mode): this;

Set cell color mode: 'sampled' (from source) or 'fixed'.

Parameters

ParameterTypeDescription
mode"sampled" | "fixed"The cell color mode

Returns

this

This instance for chaining.

Example

javascript
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let sampledSource;
let fixedSource;

function createColorCanvas() {
	const canvas = document.createElement('canvas');
	canvas.width = 160;
	canvas.height = 160;

	const ctx = canvas.getContext('2d');
	if (!ctx) return canvas;

	const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
	gradient.addColorStop(0, '#00c2ff');
	gradient.addColorStop(0.5, '#00ff88');
	gradient.addColorStop(1, '#ffee00');
	ctx.fillStyle = gradient;
	ctx.fillRect(0, 0, canvas.width, canvas.height);

	return canvas;
}

function drawLabel(text, x, y) {
	t.push();
	t.translate(x - Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(() => {
	const canvas = createColorCanvas();

	sampledSource = t.createTexture(canvas);
	sampledSource.characters(' .:-=+*#%@');
	sampledSource.charColorMode('sampled');
	sampledSource.cellColorMode('sampled');

	fixedSource = t.createTexture(canvas);
	fixedSource.characters(' .:-=+*#%@');
	fixedSource.charColorMode('sampled');
	fixedSource.cellColorMode('fixed');
	fixedSource.cellColor('#120022');
});

t.draw(() => {
	t.background(0);
	if (!sampledSource || !fixedSource) return;

	const pulse = 64 + Math.round(64 * (1 + Math.sin(t.frameCount * 0.05)));
	fixedSource.cellColor(pulse, 0, 40);

	const size = Math.min(sampledSource.width, sampledSource.height) * 0.7;
	const offset = Math.floor(size * 0.7);

	t.push();
	t.translate(-offset, 0);
	t.image(sampledSource, size, size);
	t.pop();

	t.push();
	t.translate(offset, 0);
	t.image(fixedSource, size, size);
	t.pop();

	drawLabel("cellColorMode('sampled')", -offset, Math.floor(t.grid.rows / 2) - 2);
	drawLabel("cellColorMode('fixed')", offset, Math.floor(t.grid.rows / 2) - 2);
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

Inherited from

TextmodeTexture.cellColorMode


characters()

ts
characters(chars): this;

Define the characters to use for brightness mapping as a string. Maximum length is 255; excess characters are ignored.

Parameters

ParameterTypeDescription
charsstringString of characters to map

Returns

this

This instance for chaining.

Example

javascript
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let sparseSource;
let denseSource;

function createGradientCanvas() {
	const canvas = document.createElement('canvas');
	canvas.width = 160;
	canvas.height = 160;

	const ctx = canvas.getContext('2d');
	if (!ctx) return canvas;

	const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
	gradient.addColorStop(0, '#000000');
	gradient.addColorStop(1, '#ffffff');
	ctx.fillStyle = gradient;
	ctx.fillRect(0, 0, canvas.width, canvas.height);

	return canvas;
}

function drawLabel(text, x, y) {
	t.push();
	t.translate(x - Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(() => {
	const canvas = createGradientCanvas();

	sparseSource = t.createTexture(canvas);
	sparseSource.characters(' .oO@');

	denseSource = t.createTexture(canvas);
	denseSource.characters(' .:-=+*#%@');
});

t.draw(() => {
	t.background(0);
	if (!sparseSource || !denseSource) return;

	const size = Math.min(sparseSource.width, sparseSource.height) * 0.7;
	const offset = Math.floor(size * 0.7);

	t.push();
	t.translate(-offset, 0);
	t.image(sparseSource, size, size);
	t.pop();

	t.push();
	t.translate(offset, 0);
	t.image(denseSource, size, size);
	t.pop();

	drawLabel("characters(' .oO@')", -offset, Math.floor(t.grid.rows / 2) - 2);
	drawLabel("characters(' .:-=+*#%@')", offset, Math.floor(t.grid.rows / 2) - 2);
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

Inherited from

TextmodeTexture.characters


charColor()

ts
charColor(
   colorOrGray, 
   g?, 
   b?, 
   a?): this;

Defines the character color when charColorMode is 'fixed'.

Parameters

ParameterTypeDescription
colorOrGraystring | number | TextmodeColorA grayscale value (0-255), hex string ('#RGB', '#RRGGBB', '#RRGGBBAA'), or TextmodeColor instance
g?numberOptional green component (0-255) if using RGB format, or alpha (0-255) when using grayscale form
b?numberOptional blue component (0-255) if using RGB format
a?numberOptional alpha component (0-255) if using RGBA format

Returns

this

This instance for chaining.

Example

javascript
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let source;

function createGradientCanvas() {
	const canvas = document.createElement('canvas');
	canvas.width = 160;
	canvas.height = 160;

	const ctx = canvas.getContext('2d');
	if (!ctx) return canvas;

	const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
	gradient.addColorStop(0, '#000000');
	gradient.addColorStop(1, '#ffffff');
	ctx.fillStyle = gradient;
	ctx.fillRect(0, 0, canvas.width, canvas.height);

	return canvas;
}

function drawLabel(text, y) {
	t.push();
	t.translate(-Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(() => {
	source = t.createTexture(createGradientCanvas());
	source.characters(' .:-=+*#%@');
	source.charColorMode('fixed');
	source.cellColorMode('fixed');
	source.cellColor(0);
});

t.draw(() => {
	t.background(0);
	if (!source) return;

	const red = 150 + 100 * Math.sin(t.frameCount * 0.05);
	const blue = 150 + 100 * Math.cos(t.frameCount * 0.05);

	source.charColor(red, 100, blue);
	t.image(source, source.width, source.height);
	drawLabel('charColor(r, 100, b)', Math.floor(t.grid.rows / 2) - 2);
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

Inherited from

TextmodeTexture.charColor


charColorMode()

ts
charColorMode(mode): this;

Set character color mode: 'sampled' (from source) or 'fixed'.

Parameters

ParameterTypeDescription
mode"sampled" | "fixed"The character color mode

Returns

this

This instance for chaining.

Example

javascript
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let sampledSource;
let fixedSource;

function createColorCanvas() {
	const canvas = document.createElement('canvas');
	canvas.width = 160;
	canvas.height = 160;

	const ctx = canvas.getContext('2d');
	if (!ctx) return canvas;

	const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
	gradient.addColorStop(0, '#ffcc00');
	gradient.addColorStop(0.5, '#ff0055');
	gradient.addColorStop(1, '#3300ff');
	ctx.fillStyle = gradient;
	ctx.fillRect(0, 0, canvas.width, canvas.height);

	return canvas;
}

function drawLabel(text, x, y) {
	t.push();
	t.translate(x - Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(() => {
	const canvas = createColorCanvas();

	sampledSource = t.createTexture(canvas);
	sampledSource.characters(' .:-=+*#%@');
	sampledSource.charColorMode('sampled');
	sampledSource.cellColorMode('sampled');

	fixedSource = t.createTexture(canvas);
	fixedSource.characters(' .:-=+*#%@');
	fixedSource.charColorMode('fixed');
	fixedSource.charColor(255, 80, 80);
	fixedSource.cellColorMode('sampled');
});

t.draw(() => {
	t.background(0);
	if (!sampledSource || !fixedSource) return;

	const size = Math.min(sampledSource.width, sampledSource.height) * 0.7;
	const offset = Math.floor(size * 0.7);

	t.push();
	t.translate(-offset, 0);
	t.image(sampledSource, size, size);
	t.pop();

	t.push();
	t.translate(offset, 0);
	t.image(fixedSource, size, size);
	t.pop();

	drawLabel("charColorMode('sampled')", -offset, Math.floor(t.grid.rows / 2) - 2);
	drawLabel("charColorMode('fixed')", offset, Math.floor(t.grid.rows / 2) - 2);
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

Inherited from

TextmodeTexture.charColorMode


charRotation()

ts
charRotation(degrees): this;

Set the character rotation in degrees (0-360).

Parameters

ParameterTypeDescription
degreesnumberRotation in degrees

Returns

this

This instance for chaining.

Example

javascript
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let source;

function createCheckerCanvas() {
	const canvas = document.createElement('canvas');
	canvas.width = 160;
	canvas.height = 160;

	const ctx = canvas.getContext('2d');
	if (!ctx) return canvas;

	ctx.fillStyle = '#ffffff';
	ctx.fillRect(0, 0, canvas.width, canvas.height);
	ctx.fillStyle = '#000000';
	ctx.fillRect(0, 0, canvas.width / 2, canvas.height / 2);
	ctx.fillRect(canvas.width / 2, canvas.height / 2, canvas.width / 2, canvas.height / 2);

	return canvas;
}

function drawLabel(text, x, y) {
	t.push();
	t.translate(x - Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(() => {
	source = t.createTexture(createCheckerCanvas());
	source.characters(' .:-=+*#%@');
});

t.draw(() => {
	t.background(0);
	if (!source) return;

	const size = Math.min(source.width, source.height) * 0.7;
	const offset = Math.floor(size * 0.7);

	source.charRotation(0);
	t.push();
	t.translate(-offset, 0);
	t.image(source, size, size);
	t.pop();

	source.charRotation(90);
	t.push();
	t.translate(offset, 0);
	t.image(source, size, size);
	t.pop();

	drawLabel('charRotation(0)', -offset, Math.floor(t.grid.rows / 2) - 2);
	drawLabel('charRotation(90)', offset, Math.floor(t.grid.rows / 2) - 2);
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

Inherited from

TextmodeTexture.charRotation


conversionMode()

ts
conversionMode(mode): this;

Select the conversion mode for this source.

textmode.js includes only a single built-in conversion strategy 'brightness'.

Additional conversion strategies may be provided via add-on libraries.

Parameters

ParameterTypeDescription
modestringConversion mode to use.

Returns

this

This instance for chaining.

Example

javascript
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let source;

function createGradientCanvas() {
	const canvas = document.createElement('canvas');
	canvas.width = 160;
	canvas.height = 160;

	const ctx = canvas.getContext('2d');
	if (!ctx) return canvas;

	const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
	gradient.addColorStop(0, '#040404');
	gradient.addColorStop(0.5, '#888888');
	gradient.addColorStop(1, '#f8f8f8');
	ctx.fillStyle = gradient;
	ctx.fillRect(0, 0, canvas.width, canvas.height);

	return canvas;
}

function drawLabel(text, y) {
	t.push();
	t.translate(-Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(() => {
	source = t.createTexture(createGradientCanvas());
	source.conversionMode('brightness');
	source.characters(' .:-=+*#%@');
});

t.draw(() => {
	t.background(0);
	if (!source) return;

	t.image(source, source.width, source.height);
	drawLabel("conversionMode('brightness')", Math.floor(t.grid.rows / 2) - 2);
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

Inherited from

TextmodeTexture.conversionMode


dispose()

ts
dispose(): void;

Dispose of the resource and free associated WebGL textures.

This should be called when the resource is no longer needed to prevent memory leaks. Resources created via Textmodifier.loadImage, Textmodifier.loadVideo, and Textmodifier.createTexture are automatically disposed when the Textmodifier instance is destroyed, but you can call this manually to free memory earlier.

Returns

void

Overrides

TextmodeTexture.dispose


flipX()

ts
flipX(v): this;

Set horizontal flip indicator flag.

Parameters

ParameterTypeDefault valueDescription
vnumber | booleantrueFlip flag

Returns

this

This instance for chaining.

Example

javascript
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let source;

function createArrowCanvas() {
	const canvas = document.createElement('canvas');
	canvas.width = 160;
	canvas.height = 160;

	const ctx = canvas.getContext('2d');
	if (!ctx) return canvas;

	ctx.fillStyle = '#111111';
	ctx.fillRect(0, 0, canvas.width, canvas.height);
	ctx.fillStyle = '#ffffff';
	ctx.beginPath();
	ctx.moveTo(24, 80);
	ctx.lineTo(104, 28);
	ctx.lineTo(104, 60);
	ctx.lineTo(136, 60);
	ctx.lineTo(136, 100);
	ctx.lineTo(104, 100);
	ctx.lineTo(104, 132);
	ctx.closePath();
	ctx.fill();

	return canvas;
}

function drawLabel(text, x, y) {
	t.push();
	t.translate(x - Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(() => {
	source = t.createTexture(createArrowCanvas());
	source.characters(' .:-=+*#%@');
});

t.draw(() => {
	t.background(5, 5, 15);
	if (!source) return;

	const size = Math.min(source.width, source.height) * 0.7;
	const offset = Math.floor(size * 0.7);

	source.flipX(false);
	t.push();
	t.translate(-offset, 0);
	t.image(source, size, size);
	t.pop();

	source.flipX(true);
	t.push();
	t.translate(offset, 0);
	t.image(source, size, size);
	t.pop();

	drawLabel('flipX(false)', -offset, Math.floor(t.grid.rows / 2) - 2);
	drawLabel('flipX(true)', offset, Math.floor(t.grid.rows / 2) - 2);
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

Inherited from

TextmodeTexture.flipX


flipY()

ts
flipY(v): this;

Set vertical flip indicator flag.

Parameters

ParameterTypeDefault valueDescription
vnumber | booleantrueFlip flag

Returns

this

This instance for chaining.

Example

javascript
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let source;

function createArrowCanvas() {
	const canvas = document.createElement('canvas');
	canvas.width = 160;
	canvas.height = 160;

	const ctx = canvas.getContext('2d');
	if (!ctx) return canvas;

	ctx.fillStyle = '#101010';
	ctx.fillRect(0, 0, canvas.width, canvas.height);
	ctx.fillStyle = '#ffffff';
	ctx.beginPath();
	ctx.moveTo(80, 24);
	ctx.lineTo(132, 104);
	ctx.lineTo(100, 104);
	ctx.lineTo(100, 136);
	ctx.lineTo(60, 136);
	ctx.lineTo(60, 104);
	ctx.lineTo(28, 104);
	ctx.closePath();
	ctx.fill();

	return canvas;
}

function drawLabel(text, x, y) {
	t.push();
	t.translate(x - Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(() => {
	source = t.createTexture(createArrowCanvas());
	source.characters(' .:-=+*#%@');
});

t.draw(() => {
	t.background(0);
	if (!source) return;

	const size = Math.min(source.width, source.height) * 0.7;
	const offset = Math.floor(size * 0.7);

	source.flipY(false);
	t.push();
	t.translate(-offset, 0);
	t.image(source, size, size);
	t.pop();

	source.flipY(true);
	t.push();
	t.translate(offset, 0);
	t.image(source, size, size);
	t.pop();

	drawLabel('flipY(false)', -offset, Math.floor(t.grid.rows / 2) - 2);
	drawLabel('flipY(true)', offset, Math.floor(t.grid.rows / 2) - 2);
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

Inherited from

TextmodeTexture.flipY


invert()

ts
invert(v): this;

Set the invert flag, swapping character and cell colors when enabled.

Parameters

ParameterTypeDefault valueDescription
vnumber | booleantrueInvert flag

Returns

this

This instance for chaining.

Example

javascript
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let normalSource;
let invertedSource;

function createGradientCanvas() {
	const canvas = document.createElement('canvas');
	canvas.width = 160;
	canvas.height = 160;

	const ctx = canvas.getContext('2d');
	if (!ctx) return canvas;

	const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
	gradient.addColorStop(0, '#050505');
	gradient.addColorStop(1, '#f5f5f5');
	ctx.fillStyle = gradient;
	ctx.fillRect(0, 0, canvas.width, canvas.height);

	return canvas;
}

function drawLabel(text, x, y) {
	t.push();
	t.translate(x - Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(() => {
	const canvas = createGradientCanvas();

	normalSource = t.createTexture(canvas);
	normalSource.characters(' .:-=+*#%@');

	invertedSource = t.createTexture(canvas);
	invertedSource.characters(' .:-=+*#%@');
	invertedSource.invert(true);
});

t.draw(() => {
	t.background(0);
	if (!normalSource || !invertedSource) return;

	const size = Math.min(normalSource.width, normalSource.height) * 0.7;
	const offset = Math.floor(size * 0.7);

	t.push();
	t.translate(-offset, 0);
	t.image(normalSource, size, size);
	t.pop();

	t.push();
	t.translate(offset, 0);
	t.image(invertedSource, size, size);
	t.pop();

	drawLabel('invert(false)', -offset, Math.floor(t.grid.rows / 2) - 2);
	drawLabel('invert(true)', offset, Math.floor(t.grid.rows / 2) - 2);
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

Inherited from

TextmodeTexture.invert


loop()

ts
loop(shouldLoop): this;

Set whether the video should loop.

Parameters

ParameterTypeDefault valueDescription
shouldLoopbooleantrueWhether to loop (defaults to true)

Returns

this

Example

javascript
const VIDEO_URL = 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4';
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let video;

function drawLabel(text, y) {
	t.push();
	t.translate(-Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(async () => {
	video = await t.loadVideo(VIDEO_URL);
	video.characters(' .:-=+*#%@');
	video.loop(false);
	await video.play();
});

t.draw(() => {
	t.background(0);
	if (!video) return;

	t.image(video);
	drawLabel('loop(false) with manual restart', Math.floor(t.grid.rows / 2) - 2);

	if (!video.isPlaying && video.currentTime >= video.duration) {
		video.time(0);
		void video.play();
	}
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

pause()

ts
pause(): void;

Pause the video.

Returns

void

Example

javascript
const VIDEO_URL = 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4';
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let video;

function drawLabel(text, y) {
	t.push();
	t.translate(-Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(async () => {
	video = await t.loadVideo(VIDEO_URL);
	video.characters(' .:-=+*#%@');
	video.loop();
	await video.play();
});

t.draw(() => {
	t.background(0);
	if (!video) return;

	t.image(video);
	drawLabel(video.isPlaying ? 'click to pause()' : 'click to resume with play()', Math.floor(t.grid.rows / 2) - 2);
});

t.mouseClicked(async () => {
	if (!video) return;

	if (video.isPlaying) {
		video.pause();
		return;
	}

	await video.play();
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

play()

ts
play(): Promise<void>;

Play the video.

Returns

Promise<void>

Promise that resolves when playback starts

Example

javascript
const VIDEO_URL = 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4';
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let video;

function drawLabel(text, y) {
	t.push();
	t.translate(-Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(async () => {
	video = await t.loadVideo(VIDEO_URL);
	video.characters(' .:-=+*#%@');
	await video.play();
	video.pause();
	video.time(0);
});

t.draw(() => {
	t.background(0);
	if (!video) return;

	t.image(video);
	drawLabel(video.isPlaying ? 'click to restart playback' : 'click to call play()', Math.floor(t.grid.rows / 2) - 2);
});

t.mouseClicked(async () => {
	if (!video) return;

	video.time(0);
	await video.play();
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

speed()

ts
speed(rate): this;

Set the playback speed.

Parameters

ParameterTypeDescription
ratenumberPlayback rate (1.0 = normal speed)

Returns

this

Example

javascript
const VIDEO_URL = 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4';
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let video;

function drawLabel(text, y) {
	t.push();
	t.translate(-Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(async () => {
	video = await t.loadVideo(VIDEO_URL);
	video.characters(' .:-=+*#%@');
	video.loop();
	await video.play();
});

t.draw(() => {
	t.background(0);
	if (!video) return;

	const halfWidth = t.grid.cols / 2;
	const normalizedX = (t.mouse.x + halfWidth) / t.grid.cols;
	const clampedX = Math.max(0, Math.min(1, normalizedX));
	const rate = 0.1 + clampedX * 3.9;

	video.speed(rate);
	t.image(video);
	drawLabel(`speed(${rate.toFixed(1)})`, Math.floor(t.grid.rows / 2) - 2);
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

stop()

ts
stop(): void;

Stop the video and reset to beginning.

Returns

void

Example

javascript
const VIDEO_URL = 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4';
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let video;
let restartTimeout = null;

function drawLabel(text, y) {
	t.push();
	t.translate(-Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(async () => {
	video = await t.loadVideo(VIDEO_URL);
	video.characters(' .:-=+*#%@');
	video.loop();
	await video.play();
});

t.draw(() => {
	t.background(0);
	if (!video) return;

	t.image(video);
	drawLabel("press 's' to stop()", Math.floor(t.grid.rows / 2) - 2);
});

t.keyPressed(() => {
	if (!video || !t.isKeyPressed('s')) return;

	video.stop();
	if (restartTimeout) clearTimeout(restartTimeout);
	restartTimeout = setTimeout(() => {
		void video.play();
		restartTimeout = null;
	}, 1000);
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

time()

ts
time(seconds): this;

Set the current time position in the video.

Parameters

ParameterTypeDescription
secondsnumberTime in seconds

Returns

this

Example

javascript
const VIDEO_URL = 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4';
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let video;

function drawLabel(text, y) {
	t.push();
	t.translate(-Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(async () => {
	video = await t.loadVideo(VIDEO_URL);
	video.characters(' .:-=+*#%@');
	video.loop();
	await video.play();
});

t.draw(() => {
	t.background(0);
	if (!video) return;

	t.image(video);
	drawLabel('click to jump with time()', Math.floor(t.grid.rows / 2) - 2);
	drawLabel(`${video.currentTime.toFixed(1)}s / ${video.duration.toFixed(1)}s`, Math.floor(t.grid.rows / 2) - 4);
});

t.mouseClicked(() => {
	if (!video) return;

	video.time(Math.random() * video.duration);
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub

volume()

ts
volume(level): this;

Set the volume.

Parameters

ParameterTypeDescription
levelnumberVolume level (0.0-1.0) Videos loaded through Textmodifier.loadVideo start muted to satisfy autoplay rules. Unmute the underlying videoElement in response to a user gesture before expecting audible volume changes.

Returns

this

Example

javascript
const VIDEO_URL = 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4';
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight });

let video;
let audioEnabled = false;

function drawLabel(text, y) {
	t.push();
	t.translate(-Math.floor(text.length / 2), y);
	t.charColor(255);

	for (let i = 0; i < text.length; i++) {
		t.push();
		t.translate(i, 0);
		t.char(text[i]);
		t.point();
		t.pop();
	}

	t.pop();
}

t.setup(async () => {
	video = await t.loadVideo(VIDEO_URL);
	video.characters(' .:-=+*#%@');
	video.loop();
	await video.play();
});

t.draw(() => {
	t.background(0);
	if (!video) return;

	t.image(video);

	if (audioEnabled) {
		const halfHeight = t.grid.rows / 2;
		const normalizedY = (t.mouse.y + halfHeight) / t.grid.rows;
		const level = 1 - Math.max(0, Math.min(1, normalizedY));

		video.volume(level);
		drawLabel(`volume(${level.toFixed(2)})`, Math.floor(t.grid.rows / 2) - 4);
	} else {
		drawLabel('click once to unmute videoElement', Math.floor(t.grid.rows / 2) - 4);
	}

	drawLabel('move vertically to change volume()', Math.floor(t.grid.rows / 2) - 2);
});

t.mouseClicked(async () => {
	if (!video || audioEnabled) return;

	video.videoElement.muted = false;
	audioEnabled = true;
	await video.play();
});

t.windowResized(() => {
	t.resizeCanvas(window.innerWidth, window.innerHeight);
});
codex avatar
@codex{ai-generated}Replace it with your own sketch, claim the credit, and climb the leaderboard.View sketch on GitHub