Back to Components
Interactive Text Pressure Effect Using Pure JavaScript and Variable Fonts
Component

Interactive Text Pressure Effect Using Pure JavaScript and Variable Fonts

CodewithLord
October 24, 2025

Create an eye-catching “Text Pressure” animation using pure JavaScript, CSS, and variable fonts. This effect dynamically adjusts font weight, width, and italics based on mouse movement, giving the illusion of text responding to user interaction."

Description

Create an eye-catching “Text Pressure” animation using pure JavaScript, CSS, and variable fonts. This effect dynamically adjusts font weight, width, and italics based on mouse movement, giving the illusion of text responding to user interaction. No external libraries required — just HTML, CSS, and JavaScript. Perfect for interactive web headers, hero sections, and modern typography experiments.

Html Code


1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Text Pressure (Pure JS)</title> 7 <link rel="stylesheet" href="style.css"> 8</head> 9<body> 10 <div class="text-container"> 11 <h1 id="textPressure" class="text-pressure-title stroke"></h1> 12 </div> 13 <script src="script.js"></script> 14</body> 15</html>

The HTML sets up a minimal structure:

A container (.text-container) centers the animated text.

The (h1) tag (#textPressure) holds the animated letters, which are generated dynamically by JavaScript.

External CSS (style.css) and JS (script.js) files handle the styling and animation logic.


Css Code


1@font-face { 2 font-family: "CompressaVF"; 3 src: url("https://res.cloudinary.com/dr6lvwubh/raw/upload/v1529908256/CompressaPRO-GX.woff2") 4 format("woff2"); 5 font-style: normal; 6} 7 8* { 9 box-sizing: border-box; 10 margin: 0; 11 padding: 0; 12} 13 14body { 15 height: 100vh; 16 display: flex; 17 justify-content: center; 18 align-items: center; 19 background: radial-gradient(circle at center, #050505, #000); 20 overflow: hidden; 21 color: #fff; 22} 23 24.text-container { 25 position: relative; 26 width: 100%; 27 height: 100%; 28 background: transparent; 29 display: flex; 30 justify-content: center; 31 align-items: center; 32} 33 34.text-pressure-title { 35 font-family: "CompressaVF", sans-serif; 36 text-transform: uppercase; 37 font-size: 8vw; 38 text-align: center; 39 user-select: none; 40 white-space: nowrap; 41 font-weight: 100; 42 width: 100%; 43 color: #ffffff; 44 transition: transform 0.2s ease; 45} 46 47/* Flex version (optional) */ 48.flex { 49 display: flex; 50 justify-content: space-between; 51} 52 53/* Stroke effect */ 54.stroke span { 55 position: relative; 56 color: #ffffff; 57} 58.stroke span::after { 59 content: attr(data-char); 60 position: absolute; 61 left: 0; 62 top: 0; 63 color: transparent; 64 -webkit-text-stroke-width: 2px; 65 -webkit-text-stroke-color: #00ffff; 66 z-index: -1; 67}

Key parts of the CSS:

Custom Font (@font-face) Loads CompressaVF, a variable font that supports width (wdth), weight (wght), and italic (ital) variations.

Background & Centering: The background uses a dark radial gradient, and Flexbox centers the text in the viewport.

Text Styling: Each character can be given a glowing stroke outline

This adds a cyberpunk-style neon glow beneath each letter.

Responsive Scaling: The font-size uses viewport width (8vw) for scalability across devices.


Javascript code


1// =================== CONFIG =================== 2const config = { 3 text: "Hello!", 4 fontFamily: "CompressaVF", 5 width: true, 6 weight: true, 7 italic: true, 8 alpha: false, 9 flex: false, 10 stroke: true, 11 scale: true, 12 textColor: "#FFFFFF", 13 strokeColor: "#00FFFF", 14 minFontSize: 24, 15}; 16 17// =================== SETUP =================== 18const container = document.querySelector(".text-container"); 19const title = document.getElementById("textPressure"); 20 21const chars = config.text.split(""); 22title.innerHTML = chars 23 .map((char) => `<span data-char="${char}">${char}</span>`) 24 .join(""); 25 26const spans = title.querySelectorAll("span"); 27 28let mouse = { x: 0, y: 0 }; 29let cursor = { x: 0, y: 0 }; 30 31const dist = (a, b) => { 32 const dx = b.x - a.x; 33 const dy = b.y - a.y; 34 return Math.sqrt(dx * dx + dy * dy); 35}; 36 37// =================== RESIZE HANDLER =================== 38function setSize() { 39 const containerRect = container.getBoundingClientRect(); 40 const newFontSize = Math.max(containerRect.width / (chars.length / 2), config.minFontSize); 41 title.style.fontSize = `${newFontSize}px`; 42 43 if (config.scale) { 44 requestAnimationFrame(() => { 45 const textRect = title.getBoundingClientRect(); 46 const yRatio = containerRect.height / textRect.height; 47 title.style.transform = `scale(1, ${yRatio})`; 48 title.style.lineHeight = yRatio; 49 }); 50 } 51} 52 53window.addEventListener("resize", setSize); 54setSize(); 55 56// =================== MOUSE TRACKING =================== 57window.addEventListener("mousemove", (e) => { 58 cursor.x = e.clientX; 59 cursor.y = e.clientY; 60}); 61 62window.addEventListener("touchmove", (e) => { 63 const t = e.touches[0]; 64 cursor.x = t.clientX; 65 cursor.y = t.clientY; 66}); 67 68// =================== ANIMATION LOOP =================== 69function animate() { 70 mouse.x += (cursor.x - mouse.x) / 15; 71 mouse.y += (cursor.y - mouse.y) / 15; 72 73 const titleRect = title.getBoundingClientRect(); 74 const maxDist = titleRect.width / 2; 75 76 spans.forEach((span) => { 77 const rect = span.getBoundingClientRect(); 78 const charCenter = { 79 x: rect.x + rect.width / 2, 80 y: rect.y + rect.height / 2, 81 }; 82 83 const d = dist(mouse, charCenter); 84 85 const getAttr = (distance, minVal, maxVal) => { 86 const val = maxVal - Math.abs((maxVal * distance) / maxDist); 87 return Math.max(minVal, val + minVal); 88 }; 89 90 const wdth = config.width ? Math.floor(getAttr(d, 5, 200)) : 100; 91 const wght = config.weight ? Math.floor(getAttr(d, 100, 900)) : 400; 92 const italVal = config.italic ? getAttr(d, 0, 1).toFixed(2) : 0; 93 const alphaVal = config.alpha ? getAttr(d, 0, 1).toFixed(2) : 1; 94 95 span.style.opacity = alphaVal; 96 span.style.fontVariationSettings = `'wght' ${wght}, 'wdth' ${wdth}, 'ital' ${italVal}`; 97 }); 98 99 requestAnimationFrame(animate); 100} 101 102animate();

The config object defines customizable parameters You can easily change the displayed text, font style, or animation behavior here. Dynamic Text Creation Each letter of "Hello!" is split and wrapped in a

Responsive Font Resizing

The setSize() function ensures that text resizes proportionally with the window size Text Pressure Effect

The animate() function calculates how far each character is from the cursor and changes its variable font attributes Continuous Animation

requestAnimationFrame(animate); ensures a smooth and efficient animation loop, running at 60fps.

Love this component?

Explore more components and build amazing UIs.

View All Components