textmode.js / fonts / TextmodeFont
Class: TextmodeFont
Manages the font used for rendering characters via TextmodeLayer.loadFont.
This class coordinates font loading, character extraction, texture atlas creation, and provides character information.
Each TextmodeLayer has its own instance of this class to allow for layer-specific font configurations.
Example
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight, fontSize: 8 });
const accentLayer = t.layers.add({ fontSize: 8, offset: [0, 6] });
let customFont;
function label(text, y, color = [220, 220, 220]) {
t.push();
t.translate(-Math.floor(text.length / 2), y);
t.charColor(color[0], color[1], color[2]);
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 () => {
customFont = await t.loadFont('../../primitives/FROGBLOCK-V2.1.ttf', false);
await accentLayer.loadFont(customFont);
});
t.draw(() => {
t.background(8, 10, 22);
label('TextmodeFont creation', -6, [255, 210, 90]);
label(`glyphs: ${customFont ? customFont.characters.length : 'loading'}`, -2);
label('base layer keeps its original font', 2, [150, 160, 190]);
});
accentLayer.draw(() => {
t.clear();
label('custom font on another layer', 0, [120, 220, 255]);
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});
Extends
Disposable
Implements
TextmodeGlyphAtlas
Accessors
cellDimensions
Get Signature
get cellDimensions(): object;Returns the effective glyph cell dimensions used by the layer grid.
Returns
object
| Name | Type |
|---|---|
height | number |
width | number |
Example
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight, fontSize: 8 });
function label(text, y, color = [220, 220, 220]) {
t.push();
t.translate(-Math.floor(text.length / 2), y);
t.charColor(color[0], color[1], color[2]);
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.draw(() => {
const dimensions = t.font.cellDimensions;
t.background(7, 10, 20);
label('TextmodeFont cell metrics', -5, [255, 220, 120]);
label(`cell ${dimensions.width} x ${dimensions.height}px`, -1);
label(`width ${t.font.cellWidth} height ${t.font.cellHeight}`, 3, [120, 205, 255]);
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});
Implementation of
TextmodeGlyphAtlas.cellDimensionscellHeight
Get Signature
get cellHeight(): number;Returns the effective glyph cell height used by the layer grid.
Returns
number
Example
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight, fontSize: 8 });
function label(text, y, color = [220, 220, 220]) {
t.push();
t.translate(-Math.floor(text.length / 2), y);
t.charColor(color[0], color[1], color[2]);
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.draw(() => {
const dimensions = t.font.cellDimensions;
t.background(7, 10, 20);
label('TextmodeFont cell metrics', -5, [255, 220, 120]);
label(`cell ${dimensions.width} x ${dimensions.height}px`, -1);
label(`width ${t.font.cellWidth} height ${t.font.cellHeight}`, 3, [120, 205, 255]);
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});
Implementation of
TextmodeGlyphAtlas.cellHeightcellWidth
Get Signature
get cellWidth(): number;Returns the effective glyph cell width used by the layer grid.
Returns
number
Example
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight, fontSize: 8 });
function label(text, y, color = [220, 220, 220]) {
t.push();
t.translate(-Math.floor(text.length / 2), y);
t.charColor(color[0], color[1], color[2]);
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.draw(() => {
const dimensions = t.font.cellDimensions;
t.background(7, 10, 20);
label('TextmodeFont cell metrics', -5, [255, 220, 120]);
label(`cell ${dimensions.width} x ${dimensions.height}px`, -1);
label(`width ${t.font.cellWidth} height ${t.font.cellHeight}`, 3, [120, 205, 255]);
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});
Implementation of
TextmodeGlyphAtlas.cellWidthcharacterMap
Get Signature
get characterMap(): Map<string, TextmodeGlyph>;Returns the character map for O(1) lookups.
Returns
Map<string, TextmodeGlyph>
Example
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight, fontSize: 8 });
function label(text, y, color = [220, 220, 220]) {
t.push();
t.translate(-Math.floor(text.length / 2), y);
t.charColor(color[0], color[1], color[2]);
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.draw(() => {
const glyph = t.font.characterMap.get('A');
const color = glyph ? glyph.color.map((value) => Math.round(value * 255)) : [220, 220, 220];
t.background(8, 10, 22);
t.char('A');
t.charColor(color[0], color[1], color[2]);
t.rect(10, 10);
label('characterMap', -6, [255, 210, 90]);
label(`map.has('A'): ${t.font.characterMap.has('A')}`, -2);
label('fast lookup for glyph metadata', 5, [150, 160, 190]);
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});
Implementation of
TextmodeGlyphAtlas.characterMapcharacters
Get Signature
get characters(): readonly TextmodeGlyph[];Returns the array of TextmodeGlyph objects in the font.
Returns
readonly TextmodeGlyph[]
Example
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight, fontSize: 8 });
t.draw(() => {
const chars = t.font.characters.slice(0, 96);
const cols = 16;
const rows = Math.ceil(chars.length / cols);
const startX = -Math.floor(cols / 2);
const startY = -Math.floor(rows / 2);
t.background(8, 10, 22);
for (let i = 0; i < chars.length; i++) {
const glyph = chars[i];
t.push();
t.translate(startX + (i % cols), startY + Math.floor(i / cols));
t.char(glyph.character);
t.charColor(120 + (i % cols) * 6, 140 + (i % rows) * 10, 255 - (i % cols) * 5);
t.point();
t.pop();
}
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});
Implementation of
TextmodeGlyphAtlas.characterscolumns
Get Signature
get columns(): number;Returns the number of columns in the normalized glyph atlas.
Returns
number
Example
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight, fontSize: 8 });
function label(text, y, color = [220, 220, 220]) {
t.push();
t.translate(-Math.floor(text.length / 2), y);
t.charColor(color[0], color[1], color[2]);
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.draw(() => {
const cols = t.font.textureColumns;
t.background(8, 10, 22);
label('textureColumns', -6, [255, 210, 90]);
label(`atlas columns: ${cols}`, -2);
for (let i = 0; i < cols; i++) {
t.push();
t.translate(-cols / 2 + i, 3);
t.char('|');
t.charColor(120 + (i % 12) * 10, 180, 255);
t.line(0, -2, 0, 2);
t.pop();
}
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});
Implementation of
TextmodeGlyphAtlas.columnsfont
Get Signature
get font(): TyprFont;Returns the Typr.js font object.
Returns
TyprFont
Example
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight, fontSize: 8 });
function label(text, y, color = [220, 220, 220]) {
t.push();
t.translate(-Math.floor(text.length / 2), y);
t.charColor(color[0], color[1], color[2]);
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.draw(() => {
const font = t.font.font;
const unitsPerEm = font?.head?.unitsPerEm ?? 0;
const ascender = font?.hhea?.ascender ?? 0;
t.background(7, 10, 20);
label('TextmodeFont.font', -5, [255, 220, 120]);
label(`unitsPerEm ${unitsPerEm}`, -1);
label(`ascender ${ascender}`, 3, [120, 205, 255]);
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});
fontFramebuffer
Get Signature
get fontFramebuffer(): TextmodeFramebuffer;Returns the WebGL framebuffer containing the font texture atlas.
Returns
Example
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight, fontSize: 8 });
function label(text, y, color = [220, 220, 220]) {
t.push();
t.translate(-Math.floor(text.length / 2), y);
t.charColor(color[0], color[1], color[2]);
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.draw(() => {
const atlas = t.font.fontFramebuffer;
t.background(8, 10, 22);
label('fontFramebuffer', -5, [255, 210, 90]);
label(`atlas size: ${atlas.width} x ${atlas.height}px`, -1);
label(`grid: ${t.font.textureColumns} cols x ${t.font.textureRows} rows`, 3, [150, 160, 190]);
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});
fontSize
Get Signature
get fontSize(): number;Returns the font size used for the texture atlas.
Returns
number
Example
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight, fontSize: 8 });
const miniLayer = t.layers.add({ fontSize: 16, offset: [0, 6] });
function label(text, y, color = [220, 220, 220]) {
t.push();
t.translate(-Math.floor(text.length / 2), y);
t.charColor(color[0], color[1], color[2]);
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.draw(() => {
t.background(8, 10, 22);
label('fontSize', -6, [255, 210, 90]);
label(`base font size: ${t.layers.base.font.fontSize}`, -2);
label(`upper layer font size: ${miniLayer.font.fontSize}`, 2, [150, 160, 190]);
});
miniLayer.draw(() => {
t.clear();
label('larger layer font', 0, [120, 220, 255]);
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});
framebuffer
Get Signature
get framebuffer(): TextmodeFramebuffer;Returns the normalized glyph atlas framebuffer used by the ASCII shader.
Returns
Example
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight, fontSize: 8 });
function label(text, y, color = [220, 220, 220]) {
t.push();
t.translate(-Math.floor(text.length / 2), y);
t.charColor(color[0], color[1], color[2]);
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.draw(() => {
const atlas = t.font.fontFramebuffer;
t.background(8, 10, 22);
label('fontFramebuffer', -5, [255, 210, 90]);
label(`atlas size: ${atlas.width} x ${atlas.height}px`, -1);
label(`grid: ${t.font.textureColumns} cols x ${t.font.textureRows} rows`, 3, [150, 160, 190]);
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});
Implementation of
TextmodeGlyphAtlas.framebuffermaxGlyphDimensions
Get Signature
get maxGlyphDimensions(): object;Returns the maximum dimensions of a glyph in the font in pixels.
Returns
object
| Name | Type |
|---|---|
height | number |
width | number |
Example
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight, fontSize: 8 });
function label(text, y, color = [220, 220, 220]) {
t.push();
t.translate(-Math.floor(text.length / 2), y);
t.charColor(color[0], color[1], color[2]);
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.draw(() => {
const dims = t.font.maxGlyphDimensions;
const w = Math.max(2, Math.round(dims.width / 4));
const h = Math.max(2, Math.round(dims.height / 4));
t.background(8, 10, 22);
t.char('#');
t.charColor(120, 220, 255);
t.rect(w, h);
label('maxGlyphDimensions', -6, [255, 210, 90]);
label(`${dims.width}px x ${dims.height}px`, -2);
label('scaled box shows the max glyph bounds', 5, [150, 160, 190]);
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});
rows
Get Signature
get rows(): number;Returns the number of rows in the normalized glyph atlas.
Returns
number
Example
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight, fontSize: 8 });
function label(text, y, color = [220, 220, 220]) {
t.push();
t.translate(-Math.floor(text.length / 2), y);
t.charColor(color[0], color[1], color[2]);
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.draw(() => {
const rows = t.font.textureRows;
t.background(8, 10, 22);
label('textureRows', -6, [255, 210, 90]);
label(`atlas rows: ${rows}`, -2);
for (let i = 0; i < rows; i++) {
t.push();
t.translate(0, i - rows / 2 + 4);
t.char('-');
t.charColor(120, 170 + (i % 10) * 8, 255);
t.line(-6, 0, 6, 0);
t.pop();
}
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});
Implementation of
TextmodeGlyphAtlas.rowstextureColumns
Get Signature
get textureColumns(): number;Returns the number of columns in the texture atlas.
Returns
number
Example
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight, fontSize: 8 });
function label(text, y, color = [220, 220, 220]) {
t.push();
t.translate(-Math.floor(text.length / 2), y);
t.charColor(color[0], color[1], color[2]);
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.draw(() => {
const cols = t.font.textureColumns;
t.background(8, 10, 22);
label('textureColumns', -6, [255, 210, 90]);
label(`atlas columns: ${cols}`, -2);
for (let i = 0; i < cols; i++) {
t.push();
t.translate(-cols / 2 + i, 3);
t.char('|');
t.charColor(120 + (i % 12) * 10, 180, 255);
t.line(0, -2, 0, 2);
t.pop();
}
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});
textureRows
Get Signature
get textureRows(): number;Returns the number of rows in the texture atlas.
Returns
number
Example
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight, fontSize: 8 });
function label(text, y, color = [220, 220, 220]) {
t.push();
t.translate(-Math.floor(text.length / 2), y);
t.charColor(color[0], color[1], color[2]);
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.draw(() => {
const rows = t.font.textureRows;
t.background(8, 10, 22);
label('textureRows', -6, [255, 210, 90]);
label(`atlas rows: ${rows}`, -2);
for (let i = 0; i < rows; i++) {
t.push();
t.translate(0, i - rows / 2 + 4);
t.char('-');
t.charColor(120, 170 + (i % 10) * 8, 255);
t.line(-6, 0, 6, 0);
t.pop();
}
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});
Methods
dispose()
dispose(): void;Dispose of all resources used by this font manager.
Returns
void
Example
const t = textmode.create({ width: window.innerWidth, height: window.innerHeight, fontSize: 8 });
let tempFont;
let disposed = false;
function label(text, y, color = [220, 220, 220]) {
t.push();
t.translate(-Math.floor(text.length / 2), y);
t.charColor(color[0], color[1], color[2]);
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 () => {
tempFont = await t.loadFont('../../primitives/CHUNKY.ttf', false);
});
t.draw(() => {
t.background(8, 10, 22);
label('dispose()', -4, [255, 210, 90]);
label(disposed ? 'temporary font disposed' : 'disposing temp font after 3 seconds', -1);
label('active layer font keeps rendering normally', 2, [150, 160, 190]);
if (tempFont && !disposed && t.frameCount > 180) {
tempFont.dispose();
disposed = true;
}
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});
Overrides
Disposable.dispose