# Typography ## Loading Fonts ### System Fonts ```javascript textFont('Helvetica'); textFont('Georgia'); textFont('monospace'); ``` ### Custom Fonts (OTF/TTF/WOFF2) ```javascript let myFont; function preload() { myFont = loadFont('path/to/font.otf'); // Requires local server or CORS-enabled URL } function setup() { textFont(myFont); } ``` ### Google Fonts via CSS ```html ``` Google Fonts work without `loadFont()` but only for `text()` — not for `textToPoints()`. For particle text, you need `loadFont()` with an OTF/TTF file. ## Text Rendering ### Basic Text ```javascript textSize(32); textAlign(CENTER, CENTER); text('Hello World', width/2, height/2); ``` ### Text Properties ```javascript textSize(48); // pixel size textAlign(LEFT, TOP); // horizontal: LEFT, CENTER, RIGHT // vertical: TOP, CENTER, BOTTOM, BASELINE textLeading(40); // line spacing (for multi-line text) textStyle(BOLD); // NORMAL, BOLD, ITALIC, BOLDITALIC textWrap(WORD); // WORD or CHAR (for text() with max width) ``` ### Text Metrics ```javascript let w = textWidth('Hello'); // pixel width of string let a = textAscent(); // height above baseline let d = textDescent(); // height below baseline let totalH = a + d; // full line height ``` ### Text Bounding Box ```javascript let bounds = myFont.textBounds('Hello', x, y, size); // bounds = { x, y, w, h } // Useful for positioning, collision, background rectangles ``` ### Multi-Line Text ```javascript // With max width — auto wraps textWrap(WORD); text('Long text that wraps within the given width', x, y, maxWidth); // With max width AND height — clips text('Very long text', x, y, maxWidth, maxHeight); ``` ## textToPoints() — Text as Particles Convert text outline to array of points. Requires a loaded font (OTF/TTF via `loadFont()`). ```javascript let font; let points; function preload() { font = loadFont('font.otf'); // MUST be loadFont, not CSS } function setup() { createCanvas(1200, 600); points = font.textToPoints('HELLO', 100, 400, 200, { sampleFactor: 0.1, // lower = more points (0.1-0.5 typical) simplifyThreshold: 0 }); } function draw() { background(0); for (let pt of points) { let n = noise(pt.x * 0.01, pt.y * 0.01, frameCount * 0.01); fill(255, n * 255); noStroke(); ellipse(pt.x + random(-2, 2), pt.y + random(-2, 2), 3); } } ``` ### Particle Text Class ```javascript class TextParticle { constructor(target) { this.target = createVector(target.x, target.y); this.pos = createVector(random(width), random(height)); this.vel = createVector(0, 0); this.acc = createVector(0, 0); this.maxSpeed = 10; this.maxForce = 0.5; } arrive() { let desired = p5.Vector.sub(this.target, this.pos); let d = desired.mag(); let speed = d < 100 ? map(d, 0, 100, 0, this.maxSpeed) : this.maxSpeed; desired.setMag(speed); let steer = p5.Vector.sub(desired, this.vel); steer.limit(this.maxForce); this.acc.add(steer); } flee(target, radius) { let d = this.pos.dist(target); if (d < radius) { let desired = p5.Vector.sub(this.pos, target); desired.setMag(this.maxSpeed); let steer = p5.Vector.sub(desired, this.vel); steer.limit(this.maxForce * 2); this.acc.add(steer); } } update() { this.vel.add(this.acc); this.vel.limit(this.maxSpeed); this.pos.add(this.vel); this.acc.mult(0); } display() { fill(255); noStroke(); ellipse(this.pos.x, this.pos.y, 3); } } // Usage: particles form text, scatter from mouse let textParticles = []; for (let pt of points) { textParticles.push(new TextParticle(pt)); } function draw() { background(0); for (let p of textParticles) { p.arrive(); p.flee(createVector(mouseX, mouseY), 80); p.update(); p.display(); } } ``` ## Kinetic Typography ### Wave Text ```javascript function waveText(str, x, y, size, amplitude, frequency) { textSize(size); textAlign(LEFT, BASELINE); let xOff = 0; for (let i = 0; i < str.length; i++) { let yOff = sin(frameCount * 0.05 + i * frequency) * amplitude; text(str[i], x + xOff, y + yOff); xOff += textWidth(str[i]); } } ``` ### Typewriter Effect ```javascript class Typewriter { constructor(str, x, y, speed = 50) { this.str = str; this.x = x; this.y = y; this.speed = speed; // ms per character this.startTime = millis(); this.cursor = true; } display() { let elapsed = millis() - this.startTime; let chars = min(floor(elapsed / this.speed), this.str.length); let visible = this.str.substring(0, chars); textAlign(LEFT, TOP); text(visible, this.x, this.y); // Blinking cursor if (chars < this.str.length && floor(millis() / 500) % 2 === 0) { let cursorX = this.x + textWidth(visible); line(cursorX, this.y, cursorX, this.y + textAscent() + textDescent()); } } isDone() { return millis() - this.startTime >= this.str.length * this.speed; } } ``` ### Character-by-Character Animation ```javascript function animatedText(str, x, y, size, delay = 50) { textSize(size); textAlign(LEFT, BASELINE); let xOff = 0; for (let i = 0; i < str.length; i++) { let charStart = i * delay; let t = constrain((millis() - charStart) / 500, 0, 1); let et = easeOutElastic(t); push(); translate(x + xOff, y); scale(et); let alpha = t * 255; fill(255, alpha); text(str[i], 0, 0); pop(); xOff += textWidth(str[i]); } } ``` ## Text as Mask ```javascript let textBuffer; function setup() { createCanvas(800, 800); textBuffer = createGraphics(width, height); textBuffer.background(0); textBuffer.fill(255); textBuffer.textSize(200); textBuffer.textAlign(CENTER, CENTER); textBuffer.text('MASK', width/2, height/2); } function draw() { // Draw content background(0); // ... render something colorful // Apply text mask (show content only where text is white) loadPixels(); textBuffer.loadPixels(); for (let i = 0; i < pixels.length; i += 4) { let maskVal = textBuffer.pixels[i]; // white = show, black = hide pixels[i + 3] = maskVal; // set alpha from mask } updatePixels(); } ``` ## Responsive Text Sizing ```javascript function responsiveTextSize(baseSize, baseWidth = 1920) { return baseSize * (width / baseWidth); } // Usage textSize(responsiveTextSize(48)); text('Scales with canvas', width/2, height/2); ```