textmode.js / TextmodeShader
Class: TextmodeShader
WebGL shader program created by Textmodifier.createFilterShader or Textmodifier.createShader.
Use shaders and set uniforms via Textmodifier.shader, Textmodifier.setUniform, and Textmodifier.setUniforms.
After using a custom shader, you can revert to the default textmode shader with Textmodifier.resetShader.
Extends
Disposable
Accessors
program
Get Signature
ts
get program(): WebGLProgram;Get the WebGL program
Returns
WebGLProgram
Example
javascript
const t = textmode.create({
width: window.innerWidth,
height: window.innerHeight,
fontSize: 16,
});
const labelLayer = t.layers.add();
let customShader = null;
function drawText(text, x, y, r = 220, g = 230, b = 255) {
t.push();
t.translate(x, y);
t.charColor(r, g, b);
for (let i = 0; i < text.length; i++) {
t.char(text[i]);
t.point();
t.translate(1, 0);
}
t.pop();
}
t.setup(async () => {
customShader = 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() {
vec2 centered = v_uv - 0.5;
float rings = sin(length(centered) * 42.0 - u_time * 3.0);
float scan = sin((v_uv.x + v_uv.y) * 26.0 + u_time * 2.0);
float glyph = step(0.0, rings + scan * 0.45);
o_character = vec4(glyph, 0.0, 0.0, 1.0);
o_primaryColor = vec4(0.28 + glyph * 0.6, 0.82, 1.0, 1.0);
o_secondaryColor = vec4(0.02, 0.04, 0.09, 1.0);
}`);
});
t.draw(() => {
t.background(4, 7, 18);
if (!customShader) return;
t.shader(customShader);
t.setUniform('u_time', t.frameCount * 0.025);
t.rect(t.grid.cols, t.grid.rows);
t.resetShader();
});
labelLayer.draw(() => {
t.clear();
const left = -Math.floor(t.grid.cols / 2);
const top = -Math.floor(t.grid.rows / 2);
let y = top + 3;
const x = left + 3;
drawText('TEXTMODESHADER.PROGRAM', x, y++, 100, 255, 140);
drawText('------------------------------------', x, y++, 80, 100, 150);
drawText('CONCEPT: WEBGL PROGRAM HANDLE', x, y++, 100, 220, 255);
drawText('Filter shader draws the grid.', x, y++, 140, 160, 190);
drawText('program exposes the raw handle.', x, y++, 140, 160, 190);
drawText('------------------------------------', x, y++, 80, 100, 150);
const ready = customShader && customShader.program instanceof WebGLProgram;
const state = ready ? 'READY' : 'WAIT';
drawText(`PROGRAM: ${state}`, x, y++, 140, 255, 180);
drawText('TYPE: WEBGLPROGRAM', x, y++, 255, 225, 140);
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});Methods
dispose()
ts
dispose(): void;Dispose of WebGL resources used by this shader.
Returns
void
Example
javascript
const t = textmode.create({
width: window.innerWidth,
height: window.innerHeight,
fontSize: 16,
});
const labelLayer = t.layers.add();
let customShader = null;
let isDisposed = false;
function drawText(text, x, y, r = 200, g = 220, b = 255) {
t.push();
t.translate(x, y);
t.charColor(r, g, b);
for (let i = 0; i < text.length; i++) {
t.char(text[i]);
t.point();
t.translate(1, 0);
}
t.pop();
}
async function createShader() {
const vert = `#version 300 es
in vec4 a_position; in vec2 a_uv; out vec2 v_uv;
void main() { gl_Position = a_position; v_uv = a_uv; }`;
const frag = `#version 300 es
precision highp float; in vec2 v_uv;
layout(location = 0) out vec4 o_char;
layout(location = 1) out vec4 o_fg;
layout(location = 2) out vec4 o_bg;
void main() {
float s = step(0.5, fract((v_uv.x + v_uv.y) * 10.0));
o_char = vec4(s, 0.0, 0.0, 0.0);
o_fg = vec4(1.0, 0.75 - s * 0.3, 0.25 + s * 0.4, 1.0);
o_bg = vec4(0.03, 0.05, 0.1, 1.0);
}`;
customShader = await t.createShader(vert, frag);
isDisposed = false;
}
t.setup(async () => {
await createShader();
});
t.draw(() => {
t.background(6, 10, 22);
if (customShader && !isDisposed) {
t.push();
t.shader(customShader);
t.charColor(255, 180, 100);
t.rect(14, 6);
t.resetShader();
t.pop();
} else {
t.push();
t.charColor(40, 50, 80);
t.char('.');
t.rect(14, 6);
t.pop();
}
});
t.mouseClicked(async () => {
if (customShader && !isDisposed) {
customShader.dispose();
isDisposed = true;
} else {
await createShader();
}
});
labelLayer.draw(() => {
t.clear();
const left = -Math.floor(t.grid.cols / 2);
const top = -Math.floor(t.grid.rows / 2);
let y = top + 3;
const x = left + 3;
drawText('DISPOSE', x, y++, 100, 255, 140);
drawText('--------------------------------', x, y++, 80, 100, 150);
drawText('Frees the GL program handle.', x, y++, 100, 220, 255);
drawText('Click to dispose / rebuild.', x, y++, 140, 160, 190);
drawText('--------------------------------', x, y++, 80, 100, 150);
const status = isDisposed ? 'OFFLINE' : 'ACTIVE';
const sr = isDisposed ? 255 : 140;
const sg = isDisposed ? 100 : 255;
drawText(`GPU STATUS: ${status}`, x, y++, sr, sg, 140);
});
t.windowResized(() => {
t.resizeCanvas(window.innerWidth, window.innerHeight);
});Overrides
ts
Disposable.dispose