Thermo Cursor
An interactive cursor-following thermodynamic heatmap that renders heat trails in real time. Use it for immersive hero backgrounds, creative portfolios, or anywhere you want visitors to feel their presence on the page.
Generated using Grepped's AI UI component generator — created from scratch, not pulled from a library.
cursorheatmapinteractivethermodynamic
Live Preview — customize or regenerate this in the workspace
Loading preview…
Design Intent
An interactive cursor-following thermodynamic heatmap effect for immersive background visuals.
CSS
css
body { margin: 0; min-height: 100vh; background: #050505; overflow: hidden; }
canvas { display: block; width: 100%; height: 100%; }HTML
html
<canvas id="c"></canvas>Full Source
html
<!DOCTYPE html>
<html>
<head>
<style>
body { margin: 0; min-height: 100vh; background: #050505; overflow: hidden; }
canvas { display: block; width: 100%; height: 100%; }
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d", { alpha: false });
var resolution = 12;
var coolingFactor = 0.96;
var grid, cols, rows, w, h;
var mouse = { x: -1000, y: -1000, px: -1000, py: -1000, active: false };
function getThermalColor(t) {
var r = Math.min(255, Math.max(0, t * 2.5 * 255));
var g = Math.min(255, Math.max(0, (t * 2.5 - 1) * 255));
var b = Math.min(255, Math.max(0, (t * 2.5 - 2) * 255 + t * 50));
return "rgb(" + (r + 10) + "," + (g + 10) + "," + (b + 15) + ")";
}
function resize() {
w = canvas.width = window.innerWidth;
h = canvas.height = window.innerHeight;
cols = Math.ceil(w / resolution);
rows = Math.ceil(h / resolution);
grid = new Float32Array(cols * rows);
}
document.addEventListener("mousemove", function(e) {
mouse.x = e.clientX; mouse.y = e.clientY; mouse.active = true;
});
document.addEventListener("mouseleave", function() { mouse.active = false; });
function update() {
if (mouse.active) {
var dx = mouse.x - mouse.px, dy = mouse.y - mouse.py;
var dist = Math.sqrt(dx * dx + dy * dy);
var steps = Math.ceil(dist / (resolution / 2));
for (var s = 0; s <= steps; s++) {
var t = steps > 0 ? s / steps : 0;
var mx = mouse.px + dx * t, my = mouse.py + dy * t;
var col = Math.floor(mx / resolution), row = Math.floor(my / resolution);
var radius = 2;
for (var i = -radius; i <= radius; i++) {
for (var j = -radius; j <= radius; j++) {
var c = col + i, r = row + j;
if (c >= 0 && c < cols && r >= 0 && r < rows) {
var d = Math.sqrt(i * i + j * j);
if (d <= radius) grid[c + r * cols] = Math.min(1.0, grid[c + r * cols] + 0.3 * (1 - d / radius));
}
}
}
}
}
mouse.px = mouse.x; mouse.py = mouse.y;
ctx.fillStyle = "#050505";
ctx.fillRect(0, 0, w, h);
for (var r = 0; r < rows; r++) {
for (var c = 0; c < cols; c++) {
var idx = c + r * cols;
var temp = grid[idx];
grid[idx] *= coolingFactor;
if (temp > 0.05) {
var x = c * resolution, y = r * resolution;
var size = resolution * (0.8 + temp * 0.5);
var offset = (resolution - size) / 2;
ctx.fillStyle = getThermalColor(temp);
ctx.fillRect(x + offset, y + offset, size, size);
} else if (c % 2 === 0 && r % 2 === 0) {
ctx.fillStyle = "#18181b";
ctx.fillRect(c * resolution + resolution / 2 - 1, r * resolution + resolution / 2 - 1, 2, 2);
}
}
}
requestAnimationFrame(update);
}
window.addEventListener("resize", resize);
resize();
update();
</script>
</body>
</html>