Semantic Warp Lens
🔵 InteractiveA gorgeous, high-fidelity monospaced text grid that warps, rotates, and inverts colors like an optical lens centered directly on the user's cursor.
Responsive Horizontal Leaderboard
Slot ID: ca-pub-detail-below-preview
Interactive Settings
Medium Rectangle Block
Slot ID: ca-pub-detail-after-customization
Quick Copy Actions
Download File Packages
Keyboard Shortcuts
Responsive Horizontal Leaderboard
Slot ID: ca-pub-detail-before-related
More in Interactive & Cursor-Driven
Loading Canvas Shader...
Magnetic Fluid Grid
A highly responsive minimalist geometric grid that warps, bends, and reacts under the user's cursor using custom spring-mass physics and high-performance distance calculation.
Loading Canvas Shader...
Ethereal Smoke Trails
An ultra-responsive interactive canvas trail that renders smooth, glowing fluid-like ribbons accompanied by drifting and slowly dissolving micro-smoke particles.
Loading Canvas Shader...
Kinetic Lissajous Matrix
A mathematically precise, symmetrical geometric mandala that seamlessly morphs and folds into itself in response to your cursor coordinates.
Semantic Warp Lens - HTML5 Canvas & JavaScript Interactive & Cursor-Driven background for React & Tailwind
Integrate the Semantic Warp Lens directly into your website. This asset is rendered using HTML5 Canvas 2D render loop. It is optimized for zero layout-shifts and runs with high-performance hardware-accelerated processing.
⚡ Performance Specifications
- Render Mode: INTERACTIVE (HTML5 Canvas & JavaScript)
- Fluidity: Locked at 60fps dynamic loop
- Bundle Footprint: Zero external NPM dependencies
- SEO Indexing status: 100% Crawlable static semantic HTML
🛠️ Integration Capabilities
Our templates expose inline design tokens like --color-1 and --color-2 for infinite color palettes. This template is designed to fit inside hero elements, full-screen landing pages, and interactive presentation cards.
Technical Code Reference & Syntaxes for Googlebot & Crawlers
The snippets below display the direct, unminified source code utilized for rendering this background.
HTML & Inline CSS Snippet (Vanilla)
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Semantic Warp Lens</title>
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
background: #09090b;
}
.semantic-lens-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #08090c;
overflow: hidden;
}
#canvas-semantic-warp-lens {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: none;
display: block;
}
</style>
</head>
<body>
<div class="semantic-lens-container">
<canvas id="canvas-semantic-warp-lens"></canvas>
</div>
<script>
(function() {
const canvas = document.getElementById('canvas-semantic-warp-lens');
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
let cols = 0;
let rows = 0;
let nodes = [];
const spacingX = 14;
const spacingY = 22;
const textPattern = "SEMANTIC-WARP-LENS-MOTION-CANVAS-CREATIVE-DYNAMICS-60FPS-PLAYGROUND-";
const springK = 0.082;
const friction = 0.81;
const radius = 120;
const rSq = radius * radius;
let mouseX = -1000;
let mouseY = -1000;
let targetMouseX = -1000;
let targetMouseY = -1000;
function initNodes() {
const width = canvas.width;
const height = canvas.height;
cols = Math.ceil(width / spacingX) + 1;
rows = Math.ceil(height / spacingY) + 1;
nodes = [];
const offsetX = (width - (cols - 1) * spacingX) / 2;
const offsetY = (height - (rows - 1) * spacingY) / 2;
let charIdx = 0;
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
const ox = offsetX + c * spacingX;
const oy = offsetY + r * spacingY;
const char = textPattern.charAt(charIdx % textPattern.length);
charIdx++;
nodes.push({
x: ox,
y: oy,
ox: ox,
oy: oy,
vx: 0,
vy: 0,
disp: 0,
mouseDist: 99999,
char: char
});
}
}
}
function resize() {
const rect = canvas.parentNode ? canvas.parentNode.getBoundingClientRect() : null;
const width = rect ? rect.width : window.innerWidth;
const height = rect ? rect.height : window.innerHeight;
if (canvas.width !== width || canvas.height !== height) {
canvas.width = width;
canvas.height = height;
initNodes();
}
}
function onMouseMove(e) {
const rect = canvas.getBoundingClientRect();
targetMouseX = e.clientX - rect.left;
targetMouseY = e.clientY - rect.top;
}
function onMouseLeave() {
targetMouseX = -1000;
targetMouseY = -1000;
}
function onTouchMove(e) {
if (e.touches.length > 0) {
const touch = e.touches[0];
const rect = canvas.getBoundingClientRect();
targetMouseX = touch.clientX - rect.left;
targetMouseY = touch.clientY - rect.top;
}
}
function onTouchEnd() {
targetMouseX = -1000;
targetMouseY = -1000;
}
window.addEventListener('resize', resize);
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mouseleave', onMouseLeave);
window.addEventListener('touchmove', onTouchMove, { passive: true });
window.addEventListener('touchstart', onTouchMove, { passive: true });
window.addEventListener('touchend', onTouchEnd);
window.addEventListener('touchcancel', onTouchEnd);
let observer;
if (canvas.parentNode) {
observer = new MutationObserver(resize);
observer.observe(canvas.parentNode, { attributes: true });
}
resize();
function cleanup() {
window.removeEventListener('resize', resize);
window.removeEventListener('mousemove', onMouseMove);
window.removeEventListener('mouseleave', onMouseLeave);
window.removeEventListener('touchmove', onTouchMove);
window.removeEventListener('touchstart', onTouchMove);
window.removeEventListener('touchend', onTouchEnd);
window.removeEventListener('touchcancel', onTouchEnd);
if (observer) {
observer.disconnect();
}
}
function update() {
if (targetMouseX === -1000) {
mouseX = -1000;
mouseY = -1000;
} else {
if (mouseX === -1000) {
mouseX = targetMouseX;
mouseY = targetMouseY;
} else {
mouseX += (targetMouseX - mouseX) * 0.16;
mouseY += (targetMouseY - mouseY) * 0.16;
}
}
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
let tx = node.ox;
let ty = node.oy;
let dist = 99999;
if (mouseX !== -1000) {
const dx = node.ox - mouseX;
const dy = node.oy - mouseY;
const distSq = dx * dx + dy * dy;
if (distSq < rSq) {
dist = Math.sqrt(distSq) || 0.001;
const t = dist / radius;
const pull = (1.0 - t) * (1.0 - t) * 0.65;
// Warp towards cursor
let rx = -dx * pull;
let ry = -dy * pull;
// Inside absolute center: gravity spiral swirl
if (dist < 40) {
const swirl = (1.0 - dist / 40) * 0.5;
const cosS = Math.cos(swirl);
const sinS = Math.sin(swirl);
const rxNew = rx * cosS - ry * sinS;
const ryNew = rx * sinS + ry * cosS;
rx = rxNew;
ry = ryNew;
}
tx = node.ox + rx;
ty = node.oy + ry;
}
}
const ax = (tx - node.x) * springK;
const ay = (ty - node.y) * springK;
node.vx = (node.vx + ax) * friction;
node.vy = (node.vy + ay) * friction;
node.x += node.vx;
node.y += node.vy;
node.disp = Math.hypot(node.x - node.ox, node.y - node.oy);
node.mouseDist = dist;
}
}
function draw() {
ctx.fillStyle = '#08090b';
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (mouseX !== -1000) {
ctx.beginPath();
const glow = ctx.createRadialGradient(mouseX, mouseY, 0, mouseX, mouseY, radius * 1.5);
glow.addColorStop(0, 'rgba(168, 85, 247, 0.08)');
glow.addColorStop(0.5, 'rgba(56, 189, 248, 0.02)');
glow.addColorStop(1, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = glow;
ctx.arc(mouseX, mouseY, radius * 1.5, 0, Math.PI * 2);
ctx.fill();
}
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const normalFont = 'bold 12px "JetBrains Mono", "Fira Code", monospace';
const warpedFont = 'bold 14px "JetBrains Mono", "Fira Code", monospace';
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (node.disp < 0.1 && (mouseX === -1000 || node.mouseDist > radius)) {
ctx.font = normalFont;
ctx.fillStyle = 'rgba(148, 163, 184, 0.22)';
ctx.fillText(node.char, node.ox, node.oy);
} else {
const dist = node.mouseDist;
if (dist < radius) {
const t = 1.0 - (dist / radius);
ctx.save();
ctx.translate(node.x, node.y);
const angle = (node.vx + node.vy) * 0.035;
ctx.rotate(angle);
const scale = 1.0 + t * 0.55;
ctx.scale(scale, scale);
if (dist < 35) {
ctx.fillStyle = '#ffffff';
ctx.shadowColor = '#e879f9';
ctx.shadowBlur = 8;
ctx.font = warpedFont;
} else if (dist < 75) {
ctx.fillStyle = 'rgba(168, 85, 247, 0.95)';
ctx.shadowBlur = 0;
ctx.font = warpedFont;
} else {
ctx.fillStyle = 'rgba(56, 189, 248, 0.75)';
ctx.shadowBlur = 0;
ctx.font = normalFont;
}
ctx.fillText(node.char, 0, 0);
ctx.restore();
} else {
ctx.font = normalFont;
const snapProgress = Math.min(node.disp / 30, 1.0);
const r = Math.floor(148 + snapProgress * (56 - 148));
const g = Math.floor(163 + snapProgress * (189 - 163));
const b = Math.floor(184 + snapProgress * (248 - 184));
const alpha = 0.2 + snapProgress * 0.25;
ctx.fillStyle = "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")";
ctx.fillText(node.char, node.x, node.y);
}
}
}
}
function render() {
if (!canvas.isConnected) {
cleanup();
return;
}
update();
draw();
requestAnimationFrame(render);
}
requestAnimationFrame(render);
})();
</script>
<script>
// Throttled interactive cursor coordinates mapping
(function() {
document.documentElement.style.setProperty('--mouse-x', '0');
document.documentElement.style.setProperty('--mouse-y', '0');
let targetX = 0, targetY = 0;
let currentX = 0, currentY = 0;
window.addEventListener('mousemove', (e) => {
targetX = (e.clientX / window.innerWidth) - 0.5;
targetY = (e.clientY / window.innerHeight) - 0.5;
});
window.addEventListener('mouseleave', () => {
targetX = 0;
targetY = 0;
});
function update() {
currentX += (targetX - currentX) * 0.08;
currentY += (targetY - currentY) * 0.08;
document.documentElement.style.setProperty('--mouse-x', currentX.toFixed(4));
document.documentElement.style.setProperty('--mouse-y', currentY.toFixed(4));
requestAnimationFrame(update);
}
requestAnimationFrame(update);
})();
</script>
</body>
</html>React Component Wrapper (TSX)
import React, { useEffect, useRef } from 'react';
export default function SemanticWarpLensBackground() {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
let targetX = 0, targetY = 0;
let currentX = 0, currentY = 0;
let frameId: number;
const handleMouseMove = (e: MouseEvent) => {
targetX = (e.clientX / window.innerWidth) - 0.5;
targetY = (e.clientY / window.innerHeight) - 0.5;
};
const handleMouseLeave = () => {
targetX = 0;
targetY = 0;
};
window.addEventListener('mousemove', handleMouseMove);
window.addEventListener('mouseleave', handleMouseLeave);
const updateCoordinates = () => {
currentX += (targetX - currentX) * 0.08;
currentY += (targetY - currentY) * 0.08;
if (containerRef.current) {
containerRef.current.style.setProperty('--mouse-x', currentX.toFixed(4));
containerRef.current.style.setProperty('--mouse-y', currentY.toFixed(4));
}
frameId = requestAnimationFrame(updateCoordinates);
};
frameId = requestAnimationFrame(updateCoordinates);
return () => {
window.removeEventListener('mousemove', handleMouseMove);
window.removeEventListener('mouseleave', handleMouseLeave);
cancelAnimationFrame(frameId);
};
}, []);
return (
<div
ref={containerRef}
style={{ width: '100%', height: '100%', position: 'relative', overflow: 'hidden' }}
>
<style dangerouslySetInnerHTML={{ __html: `
.semantic-lens-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #08090c;
overflow: hidden;
}
#canvas-semantic-warp-lens {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: none;
display: block;
}
` }} />
{/* HTML Structure */}
<div
style={{ width: '100%', height: '100%' }}
dangerouslySetInnerHTML={{ __html: `
<div class="semantic-lens-container">
<canvas id="canvas-semantic-warp-lens"></canvas>
</div>
<script>
(function() {
const canvas = document.getElementById('canvas-semantic-warp-lens');
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
let cols = 0;
let rows = 0;
let nodes = [];
const spacingX = 14;
const spacingY = 22;
const textPattern = "SEMANTIC-WARP-LENS-MOTION-CANVAS-CREATIVE-DYNAMICS-60FPS-PLAYGROUND-";
const springK = 0.082;
const friction = 0.81;
const radius = 120;
const rSq = radius * radius;
let mouseX = -1000;
let mouseY = -1000;
let targetMouseX = -1000;
let targetMouseY = -1000;
function initNodes() {
const width = canvas.width;
const height = canvas.height;
cols = Math.ceil(width / spacingX) + 1;
rows = Math.ceil(height / spacingY) + 1;
nodes = [];
const offsetX = (width - (cols - 1) * spacingX) / 2;
const offsetY = (height - (rows - 1) * spacingY) / 2;
let charIdx = 0;
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
const ox = offsetX + c * spacingX;
const oy = offsetY + r * spacingY;
const char = textPattern.charAt(charIdx % textPattern.length);
charIdx++;
nodes.push({
x: ox,
y: oy,
ox: ox,
oy: oy,
vx: 0,
vy: 0,
disp: 0,
mouseDist: 99999,
char: char
});
}
}
}
function resize() {
const rect = canvas.parentNode ? canvas.parentNode.getBoundingClientRect() : null;
const width = rect ? rect.width : window.innerWidth;
const height = rect ? rect.height : window.innerHeight;
if (canvas.width !== width || canvas.height !== height) {
canvas.width = width;
canvas.height = height;
initNodes();
}
}
function onMouseMove(e) {
const rect = canvas.getBoundingClientRect();
targetMouseX = e.clientX - rect.left;
targetMouseY = e.clientY - rect.top;
}
function onMouseLeave() {
targetMouseX = -1000;
targetMouseY = -1000;
}
function onTouchMove(e) {
if (e.touches.length > 0) {
const touch = e.touches[0];
const rect = canvas.getBoundingClientRect();
targetMouseX = touch.clientX - rect.left;
targetMouseY = touch.clientY - rect.top;
}
}
function onTouchEnd() {
targetMouseX = -1000;
targetMouseY = -1000;
}
window.addEventListener('resize', resize);
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mouseleave', onMouseLeave);
window.addEventListener('touchmove', onTouchMove, { passive: true });
window.addEventListener('touchstart', onTouchMove, { passive: true });
window.addEventListener('touchend', onTouchEnd);
window.addEventListener('touchcancel', onTouchEnd);
let observer;
if (canvas.parentNode) {
observer = new MutationObserver(resize);
observer.observe(canvas.parentNode, { attributes: true });
}
resize();
function cleanup() {
window.removeEventListener('resize', resize);
window.removeEventListener('mousemove', onMouseMove);
window.removeEventListener('mouseleave', onMouseLeave);
window.removeEventListener('touchmove', onTouchMove);
window.removeEventListener('touchstart', onTouchMove);
window.removeEventListener('touchend', onTouchEnd);
window.removeEventListener('touchcancel', onTouchEnd);
if (observer) {
observer.disconnect();
}
}
function update() {
if (targetMouseX === -1000) {
mouseX = -1000;
mouseY = -1000;
} else {
if (mouseX === -1000) {
mouseX = targetMouseX;
mouseY = targetMouseY;
} else {
mouseX += (targetMouseX - mouseX) * 0.16;
mouseY += (targetMouseY - mouseY) * 0.16;
}
}
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
let tx = node.ox;
let ty = node.oy;
let dist = 99999;
if (mouseX !== -1000) {
const dx = node.ox - mouseX;
const dy = node.oy - mouseY;
const distSq = dx * dx + dy * dy;
if (distSq < rSq) {
dist = Math.sqrt(distSq) || 0.001;
const t = dist / radius;
const pull = (1.0 - t) * (1.0 - t) * 0.65;
// Warp towards cursor
let rx = -dx * pull;
let ry = -dy * pull;
// Inside absolute center: gravity spiral swirl
if (dist < 40) {
const swirl = (1.0 - dist / 40) * 0.5;
const cosS = Math.cos(swirl);
const sinS = Math.sin(swirl);
const rxNew = rx * cosS - ry * sinS;
const ryNew = rx * sinS + ry * cosS;
rx = rxNew;
ry = ryNew;
}
tx = node.ox + rx;
ty = node.oy + ry;
}
}
const ax = (tx - node.x) * springK;
const ay = (ty - node.y) * springK;
node.vx = (node.vx + ax) * friction;
node.vy = (node.vy + ay) * friction;
node.x += node.vx;
node.y += node.vy;
node.disp = Math.hypot(node.x - node.ox, node.y - node.oy);
node.mouseDist = dist;
}
}
function draw() {
ctx.fillStyle = '#08090b';
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (mouseX !== -1000) {
ctx.beginPath();
const glow = ctx.createRadialGradient(mouseX, mouseY, 0, mouseX, mouseY, radius * 1.5);
glow.addColorStop(0, 'rgba(168, 85, 247, 0.08)');
glow.addColorStop(0.5, 'rgba(56, 189, 248, 0.02)');
glow.addColorStop(1, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = glow;
ctx.arc(mouseX, mouseY, radius * 1.5, 0, Math.PI * 2);
ctx.fill();
}
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const normalFont = 'bold 12px "JetBrains Mono", "Fira Code", monospace';
const warpedFont = 'bold 14px "JetBrains Mono", "Fira Code", monospace';
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (node.disp < 0.1 && (mouseX === -1000 || node.mouseDist > radius)) {
ctx.font = normalFont;
ctx.fillStyle = 'rgba(148, 163, 184, 0.22)';
ctx.fillText(node.char, node.ox, node.oy);
} else {
const dist = node.mouseDist;
if (dist < radius) {
const t = 1.0 - (dist / radius);
ctx.save();
ctx.translate(node.x, node.y);
const angle = (node.vx + node.vy) * 0.035;
ctx.rotate(angle);
const scale = 1.0 + t * 0.55;
ctx.scale(scale, scale);
if (dist < 35) {
ctx.fillStyle = '#ffffff';
ctx.shadowColor = '#e879f9';
ctx.shadowBlur = 8;
ctx.font = warpedFont;
} else if (dist < 75) {
ctx.fillStyle = 'rgba(168, 85, 247, 0.95)';
ctx.shadowBlur = 0;
ctx.font = warpedFont;
} else {
ctx.fillStyle = 'rgba(56, 189, 248, 0.75)';
ctx.shadowBlur = 0;
ctx.font = normalFont;
}
ctx.fillText(node.char, 0, 0);
ctx.restore();
} else {
ctx.font = normalFont;
const snapProgress = Math.min(node.disp / 30, 1.0);
const r = Math.floor(148 + snapProgress * (56 - 148));
const g = Math.floor(163 + snapProgress * (189 - 163));
const b = Math.floor(184 + snapProgress * (248 - 184));
const alpha = 0.2 + snapProgress * 0.25;
ctx.fillStyle = "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")";
ctx.fillText(node.char, node.x, node.y);
}
}
}
}
function render() {
if (!canvas.isConnected) {
cleanup();
return;
}
update();
draw();
requestAnimationFrame(render);
}
requestAnimationFrame(render);
})();
</script>
` }}
/>
</div>
);
}Next.js App Router Component (use client)
'use client';
import React, { useEffect, useRef } from 'react';
export default function SemanticWarpLensBackground() {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
let targetX = 0, targetY = 0;
let currentX = 0, currentY = 0;
let frameId: number;
const handleMouseMove = (e: MouseEvent) => {
targetX = (e.clientX / window.innerWidth) - 0.5;
targetY = (e.clientY / window.innerHeight) - 0.5;
};
const handleMouseLeave = () => {
targetX = 0;
targetY = 0;
};
window.addEventListener('mousemove', handleMouseMove);
window.addEventListener('mouseleave', handleMouseLeave);
const updateCoordinates = () => {
currentX += (targetX - currentX) * 0.08;
currentY += (targetY - currentY) * 0.08;
if (containerRef.current) {
containerRef.current.style.setProperty('--mouse-x', currentX.toFixed(4));
containerRef.current.style.setProperty('--mouse-y', currentY.toFixed(4));
}
frameId = requestAnimationFrame(updateCoordinates);
};
frameId = requestAnimationFrame(updateCoordinates);
return () => {
window.removeEventListener('mousemove', handleMouseMove);
window.removeEventListener('mouseleave', handleMouseLeave);
cancelAnimationFrame(frameId);
};
}, []);
return (
<div
ref={containerRef}
className="w-full h-full relative overflow-hidden"
>
<style dangerouslySetInnerHTML={{ __html: `
.semantic-lens-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #08090c;
overflow: hidden;
}
#canvas-semantic-warp-lens {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: none;
display: block;
}
` }} />
{/* HTML Structure */}
<div
className="w-full h-full"
dangerouslySetInnerHTML={{ __html: `
<div class="semantic-lens-container">
<canvas id="canvas-semantic-warp-lens"></canvas>
</div>
<script>
(function() {
const canvas = document.getElementById('canvas-semantic-warp-lens');
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
let cols = 0;
let rows = 0;
let nodes = [];
const spacingX = 14;
const spacingY = 22;
const textPattern = "SEMANTIC-WARP-LENS-MOTION-CANVAS-CREATIVE-DYNAMICS-60FPS-PLAYGROUND-";
const springK = 0.082;
const friction = 0.81;
const radius = 120;
const rSq = radius * radius;
let mouseX = -1000;
let mouseY = -1000;
let targetMouseX = -1000;
let targetMouseY = -1000;
function initNodes() {
const width = canvas.width;
const height = canvas.height;
cols = Math.ceil(width / spacingX) + 1;
rows = Math.ceil(height / spacingY) + 1;
nodes = [];
const offsetX = (width - (cols - 1) * spacingX) / 2;
const offsetY = (height - (rows - 1) * spacingY) / 2;
let charIdx = 0;
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
const ox = offsetX + c * spacingX;
const oy = offsetY + r * spacingY;
const char = textPattern.charAt(charIdx % textPattern.length);
charIdx++;
nodes.push({
x: ox,
y: oy,
ox: ox,
oy: oy,
vx: 0,
vy: 0,
disp: 0,
mouseDist: 99999,
char: char
});
}
}
}
function resize() {
const rect = canvas.parentNode ? canvas.parentNode.getBoundingClientRect() : null;
const width = rect ? rect.width : window.innerWidth;
const height = rect ? rect.height : window.innerHeight;
if (canvas.width !== width || canvas.height !== height) {
canvas.width = width;
canvas.height = height;
initNodes();
}
}
function onMouseMove(e) {
const rect = canvas.getBoundingClientRect();
targetMouseX = e.clientX - rect.left;
targetMouseY = e.clientY - rect.top;
}
function onMouseLeave() {
targetMouseX = -1000;
targetMouseY = -1000;
}
function onTouchMove(e) {
if (e.touches.length > 0) {
const touch = e.touches[0];
const rect = canvas.getBoundingClientRect();
targetMouseX = touch.clientX - rect.left;
targetMouseY = touch.clientY - rect.top;
}
}
function onTouchEnd() {
targetMouseX = -1000;
targetMouseY = -1000;
}
window.addEventListener('resize', resize);
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mouseleave', onMouseLeave);
window.addEventListener('touchmove', onTouchMove, { passive: true });
window.addEventListener('touchstart', onTouchMove, { passive: true });
window.addEventListener('touchend', onTouchEnd);
window.addEventListener('touchcancel', onTouchEnd);
let observer;
if (canvas.parentNode) {
observer = new MutationObserver(resize);
observer.observe(canvas.parentNode, { attributes: true });
}
resize();
function cleanup() {
window.removeEventListener('resize', resize);
window.removeEventListener('mousemove', onMouseMove);
window.removeEventListener('mouseleave', onMouseLeave);
window.removeEventListener('touchmove', onTouchMove);
window.removeEventListener('touchstart', onTouchMove);
window.removeEventListener('touchend', onTouchEnd);
window.removeEventListener('touchcancel', onTouchEnd);
if (observer) {
observer.disconnect();
}
}
function update() {
if (targetMouseX === -1000) {
mouseX = -1000;
mouseY = -1000;
} else {
if (mouseX === -1000) {
mouseX = targetMouseX;
mouseY = targetMouseY;
} else {
mouseX += (targetMouseX - mouseX) * 0.16;
mouseY += (targetMouseY - mouseY) * 0.16;
}
}
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
let tx = node.ox;
let ty = node.oy;
let dist = 99999;
if (mouseX !== -1000) {
const dx = node.ox - mouseX;
const dy = node.oy - mouseY;
const distSq = dx * dx + dy * dy;
if (distSq < rSq) {
dist = Math.sqrt(distSq) || 0.001;
const t = dist / radius;
const pull = (1.0 - t) * (1.0 - t) * 0.65;
// Warp towards cursor
let rx = -dx * pull;
let ry = -dy * pull;
// Inside absolute center: gravity spiral swirl
if (dist < 40) {
const swirl = (1.0 - dist / 40) * 0.5;
const cosS = Math.cos(swirl);
const sinS = Math.sin(swirl);
const rxNew = rx * cosS - ry * sinS;
const ryNew = rx * sinS + ry * cosS;
rx = rxNew;
ry = ryNew;
}
tx = node.ox + rx;
ty = node.oy + ry;
}
}
const ax = (tx - node.x) * springK;
const ay = (ty - node.y) * springK;
node.vx = (node.vx + ax) * friction;
node.vy = (node.vy + ay) * friction;
node.x += node.vx;
node.y += node.vy;
node.disp = Math.hypot(node.x - node.ox, node.y - node.oy);
node.mouseDist = dist;
}
}
function draw() {
ctx.fillStyle = '#08090b';
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (mouseX !== -1000) {
ctx.beginPath();
const glow = ctx.createRadialGradient(mouseX, mouseY, 0, mouseX, mouseY, radius * 1.5);
glow.addColorStop(0, 'rgba(168, 85, 247, 0.08)');
glow.addColorStop(0.5, 'rgba(56, 189, 248, 0.02)');
glow.addColorStop(1, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = glow;
ctx.arc(mouseX, mouseY, radius * 1.5, 0, Math.PI * 2);
ctx.fill();
}
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const normalFont = 'bold 12px "JetBrains Mono", "Fira Code", monospace';
const warpedFont = 'bold 14px "JetBrains Mono", "Fira Code", monospace';
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (node.disp < 0.1 && (mouseX === -1000 || node.mouseDist > radius)) {
ctx.font = normalFont;
ctx.fillStyle = 'rgba(148, 163, 184, 0.22)';
ctx.fillText(node.char, node.ox, node.oy);
} else {
const dist = node.mouseDist;
if (dist < radius) {
const t = 1.0 - (dist / radius);
ctx.save();
ctx.translate(node.x, node.y);
const angle = (node.vx + node.vy) * 0.035;
ctx.rotate(angle);
const scale = 1.0 + t * 0.55;
ctx.scale(scale, scale);
if (dist < 35) {
ctx.fillStyle = '#ffffff';
ctx.shadowColor = '#e879f9';
ctx.shadowBlur = 8;
ctx.font = warpedFont;
} else if (dist < 75) {
ctx.fillStyle = 'rgba(168, 85, 247, 0.95)';
ctx.shadowBlur = 0;
ctx.font = warpedFont;
} else {
ctx.fillStyle = 'rgba(56, 189, 248, 0.75)';
ctx.shadowBlur = 0;
ctx.font = normalFont;
}
ctx.fillText(node.char, 0, 0);
ctx.restore();
} else {
ctx.font = normalFont;
const snapProgress = Math.min(node.disp / 30, 1.0);
const r = Math.floor(148 + snapProgress * (56 - 148));
const g = Math.floor(163 + snapProgress * (189 - 163));
const b = Math.floor(184 + snapProgress * (248 - 184));
const alpha = 0.2 + snapProgress * 0.25;
ctx.fillStyle = "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")";
ctx.fillText(node.char, node.x, node.y);
}
}
}
}
function render() {
if (!canvas.isConnected) {
cleanup();
return;
}
update();
draw();
requestAnimationFrame(render);
}
requestAnimationFrame(render);
})();
</script>
` }}
/>
</div>
);
}Raw CSS Stylesheet Snippet
.semantic-lens-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #08090c;
overflow: hidden;
}
#canvas-semantic-warp-lens {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: none;
display: block;
}