Concept:
This kinetic typography transforms static text into an interactive visual display. By treating each point of the font’s letters as a vehicle, the sketch brings the text “Dream” to life, allowing it to respond dynamically to the viewer’s mouse movements. Vehicles “arrive” at their designated spots to form the letters of the word and “flee” when the mouse comes close, creating an engaging effect that feels both chaotic and ordered. The addition of the gradient background adds depth and visual interest, making the typography a part of an aesthetically pleasing composition of a sunset.
Use your mouse to go through the word to interact with it:
Font Points Extraction and Vehicle Initialization:
//Extract points from the font
let points = font.textToPoints(textString, 75, 320, fontSize, {
sampleFactor: 0.1
});
//Initialize vehicles for each point
for (let i = 0; i < points.length; i++) {
let pt = points[i];
let vehicle = new Vehicle(pt.x, pt.y);
vehicles.push(vehicle);
}
Extracting points from text and converting them into “vehicles” was a bit complex due to the need to handle font rendering and point manipulation. The textToPoints() function is used to get the vector points for each letter, which are then used to create vehicle objects that simulate motion.
Also for the Vehicle class, implementing behaviors such as “arrive” and “flee” required understanding of steering behaviors and force accumulation in a physics-based simulation. Balancing these forces to achieve smooth and natural movement was challenging, especially when trying to simulate both attraction to a target point and repulsion from the mouse cursor.
Full code:
//Preload the font
let font;
let vehicles = []; //Array to hold the 'vehicle' objects
function preload() {
//Load the font from the assets
font = loadFont('Hogfish DEMO.otf');
}
function setup() {
createCanvas(700, 500); //Set up the canvas
//Create a background gradient
setGradient(0, 0, width, height, color('#EBC249'), color('#A52D56'), Y_AXIS);
//Define the text properties
let textString = 'Dream';
let fontSize = 180;
//Convert the text to a series of points
let points = font.textToPoints(textString, 75, 330, fontSize, {
sampleFactor: 0.1 //Determines the density of the points
});
//Create a vehicle object for each point
for (let i = 0; i < points.length; i++) {
let pt = points[i];
let vehicle = new Vehicle(pt.x, pt.y);
vehicles.push(vehicle);
}
}
function draw() {
//Refresh the gradient background each frame
setGradient(0, 0, width, height, color('#EBC249'), color('#A52D56'), Y_AXIS);
//Update and display each vehicle
for (let i = 0; i < vehicles.length; i++) {
let v = vehicles[i];
v.behaviors();
v.update();
v.show();
}
}
//Constants for gradient direction
const Y_AXIS = 1;
const X_AXIS = 2;
//Function to create a gradient background
function setGradient(x, y, w, h, c1, c2, axis) {
noFill();
//Create a vertical gradient
if (axis === Y_AXIS) {
for (let i = y; i <= y + h; i++) {
let inter = map(i, y, y + h, 0, 1);
let c = lerpColor(c1, c2, inter);
stroke(c);
line(x, i, x + w, i);
}
}
//Create a horizontal gradient
else if (axis === X_AXIS) {
for (let i = x; i <= x + w; i++) {
let inter = map(i, x, x + w, 0, 1);
let c = lerpColor(c1, c2, inter);
stroke(c);
line(i, y, i, y + h);
}
}
}
//The Vehicle class
class Vehicle {
constructor(x, y) {
this.pos = createVector(random(width), random(height)); // Start position
this.target = createVector(x, y); //Target position
this.vel = p5.Vector.random2D(); //Initial velocity
this.acc = createVector(); //Acceleration
this.r = 8; //Radius
this.maxspeed = 10; //Maximum speed
this.maxforce = 1; //Maximum steering force
}
//Combine behaviors
behaviors() {
let arrive = this.arrive(this.target);
let mouse = createVector(mouseX, mouseY);
let flee = this.flee(mouse);
arrive.mult(1);
flee.mult(5);
this.applyForce(arrive);
this.applyForce(flee);
}
//Apply a force to the vehicle
applyForce(f) {
this.acc.add(f);
}
//Update the vehicle's position
update() {
this.pos.add(this.vel);
this.vel.add(this.acc);
this.acc.mult(0); //Reset acceleration each frame
}
//Display the vehicle
show() {
stroke(255);
strokeWeight(8);
point(this.pos.x, this.pos.y);
}
//Steer the vehicle towards a target
arrive(target) {
let desired = p5.Vector.sub(target, this.pos); //A vector pointing from the position to the target
let d = desired.mag();
let speed = this.maxspeed;
if (d < 100) {
//Adjust speed based on distance to the target
speed = map(d, 0, 100, 0, this.maxspeed);
}
desired.setMag(speed);
let steer = p5.Vector.sub(desired, this.vel);
steer.limit(this.maxforce);
return steer;
}
//Make the vehicle flee from the mouse
flee(target) {
let desired = p5.Vector.sub(target, this.pos);
let d = desired.mag();
if (d < 50) {
desired.setMag(this.maxspeed);
desired.mult(-1); //Go in the opposite direction
let steer = p5.Vector.sub(desired, this.vel);
steer.limit(this.maxforce);
return steer;
} else {
return createVector(0, 0); //No force if the mouse is far away
}
}
}