My name in the hero reacts to proximity: each letter's weight and width follow the distance to the pointer, so the type itself becomes the interaction.
Each letter is an inline-block span. On every frame the pointer position is eased toward its target, and the distance from each letter's cached centre to the pointer is mapped to a variable-font weight: close letters swell toward the peak weight, distant ones rest at the base weight. Glyph centres are measured once (not per frame) and only the font-variation-settings string is written, so the loop does no layout reads. A single shared requestAnimationFrame serves every instance and goes idle once the pointer settles.
const getAttr = (distance, maxDist, minVal, maxVal) => {
const val = maxVal - Math.abs((maxVal * distance) / maxDist);
return Math.max(minVal, val + minVal);
};
// per letter, per frame
const dx = mouse.x - item.x;
const dy = mouse.y - item.y;
const d = Math.sqrt(dx * dx + dy * dy);
const wght = Math.max(
baseWeight,
Math.min(maxWeight, Math.floor(getAttr(d, maxDist, 100, maxWeight)))
);
const settings = "'wght' " + wght + ", 'wdth' 100, 'ital' 0";
if (item.elem.style.fontVariationSettings !== settings) {
item.elem.style.fontVariationSettings = settings;
}