Meta Description" name="description" />
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>Universal Neon Cat's Cradle</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.0/p5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/hands/hands.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js"></script>
<style>
html, body {
margin: 0;
padding: 0;
overflow: hidden;
background: #020816;
touch-action: none;
}
canvas {
display: block;
}
#video {
position: fixed;
top: 0;
left: 0;
opacity: 0;
pointer-events: none;
}
#status {
position: fixed;
top: 20px;
left: 20px;
color: rgba(255,255,255,0.7);
font-family: Arial;
letter-spacing: 2px;
font-size: 12px;
z-index: 10;
}
</style>
</head>
<body>
<div id="status">Initializing Camera...</div>
<video id="video" autoplay playsinline muted></video>
<script>
let video;
let hands;
let camera;
let handData = [];
let ready = false;
const fingertips = [4,8,12,16,20];
function setup() {
createCanvas(windowWidth, windowHeight);
pixelDensity(1);
video = document.getElementById("video");
startEverything();
}
async function startEverything() {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: {
facingMode: "user",
width: { ideal: 1280 },
height: { ideal: 720 }
},
audio: false
});
video.srcObject = stream;
await video.play();
setupHands();
document.getElementById("status").innerText = "Camera Ready";
} catch(err) {
console.error(err);
document.getElementById("status").innerText = "Camera Permission Failed";
}
}
function setupHands() {
hands = new Hands({
locateFile: file =>
`https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}`
});
hands.setOptions({
maxNumHands: 2,
modelComplexity: 1,
minDetectionConfidence: 0.65,
minTrackingConfidence: 0.65
});
hands.onResults(results => {
handData = [];
if (results.multiHandLandmarks) {
for (let i = 0; i < results.multiHandLandmarks.length; i++) {
handData.push({
landmarks: results.multiHandLandmarks[i],
label: results.multiHandedness[i].label
});
}
}
ready = true;
});
camera = new Camera(video, {
onFrame: async () => {
await hands.send({ image: video });
},
width: 1280,
height: 720
});
camera.start();
}
function draw() {
background(2,8,22);
drawVideo();
drawCRT();
if (!ready) return;
drawLandmarks();
drawStrings();
}
function drawVideo() {
push();
translate(width,0);
scale(-1,1);
tint(255,70);
image(video,0,0,width,height);
pop();
fill(0,10,30,110);
rect(0,0,width,height);
}
function drawLandmarks() {
for (let hand of handData) {
for (let i = 0; i < hand.landmarks.length; i++) {
let lm = hand.landmarks[i];
let x = width - lm.x * width;
let y = lm.y * height;
let c = getColor(i);
drawingContext.shadowBlur = 25;
drawingContext.shadowColor = c;
noStroke();
fill(c);
circle(x,y,10);
fill(255);
circle(x,y,3);
}
}
drawingContext.shadowBlur = 0;
}
function drawStrings() {
if (handData.length < 2) return;
let left = null;
let right = null;
for (let h of handData) {
if (h.label === "Left") left = h;
if (h.label === "Right") right = h;
}
if (!left || !right) return;
for (let i = 0; i < fingertips.length; i++) {
let a = left.landmarks[fingertips[i]];
let b = right.landmarks[fingertips[i]];
let x1 = width - a.x * width;
let y1 = a.y * height;
let x2 = width - b.x * width;
let y2 = b.y * height;
let d = dist(x1,y1,x2,y2);
let thick = map(d,50,700,8,1,true);
neonCurve(x1,y1,x2,y2,thick,d);
}
}
function neonCurve(x1,y1,x2,y2,t,distance) {
const ctx = drawingContext;
let gradient = ctx.createLinearGradient(x1,y1,x2,y2);
gradient.addColorStop(0,"#00ffff");
gradient.addColorStop(0.5,"#ff00ff");
gradient.addColorStop(1,"#ffff00");
ctx.strokeStyle = gradient;
ctx.lineWidth = t;
ctx.shadowBlur = 30;
ctx.shadowColor = "#00ffff";
noFill();
let mx = (x1+x2)/2;
let my = (y1+y2)/2;
let wave = sin(frameCount*0.08 + distance*0.01)*20;
beginShape();
curveVertex(x1,y1);
curveVertex(x1,y1);
curveVertex(mx,my+wave);
curveVertex(x2,y2);
curveVertex(x2,y2);
endShape();
ctx.shadowBlur = 0;
}
function drawCRT() {
stroke(255,8);
for (let y=0;y<height;y+=4) {
line(0,y,width,y);
}
}
function getColor(i) {
if (i % 3 === 0) return "#00ffff";
if (i % 3 === 1) return "#ff00ff";
return "#ffff00";
}
function windowResized() {
resizeCanvas(windowWidth,windowHeight);
}
</script>
</body>
</html>4
3
215KB
1063KB
621.0ms
636.0ms
622.0ms