Meta Description" name="description" />

Share this result

Previews are deleted daily. Get a permanent share link sent to your inbox:
Script
<!DOCTYPE html> <html lang="id"> <head> <meta charset="UTF-8"> <title>Love You - Pink Heart</title> <style> html, body { margin: 0; padding: 0; width: 100%; height: 100%; background: #000; overflow: hidden; } canvas { display: block; background: #000; } .hint { position: fixed; bottom: 14px; left: 0; right: 0; text-align: center; color: rgba(255,255,255,0.25); font-family: monospace; font-size: 11px; letter-spacing: 1px; } </style> </head> <body> <canvas id="c"></canvas> <div class="hint">tap layar untuk mengulang animasi</div> <script> const canvas = document.getElementById('c'); const ctx = canvas.getContext('2d'); let W, H, scale; let heartPolygon = null; let particles = []; function heartPoint(t) { const x = 16 * Math.pow(Math.sin(t), 3); const y = -(13 * Math.cos(t) - 5 * Math.cos(2*t) - 2 * Math.cos(3*t) - Math.cos(4*t)); return { x: x / 17, y: y / 17 }; } function buildHeartPolygon() { heartPolygon = []; const steps = 220; for (let i = 0; i <= steps; i++) { const t = (i / steps) * Math.PI * 2; heartPolygon.push(heartPoint(t)); } } function pointInHeartPolygon(px, py) { let inside = false; for (let i = 0, j = heartPolygon.length - 1; i < heartPolygon.length; j = i++) { const xi = heartPolygon[i].x, yi = heartPolygon[i].y; const xj = heartPolygon[j].x, yj = heartPolygon[j].y; const intersect = ((yi > py) !== (yj > py)) && (px < (xj - xi) * (py - yi) / (yj - yi) + xi); if (intersect) inside = !inside; } return inside; } // bangun satu lapisan grid teks (baris x kolom), hanya sel yg jatuh di dalam hati yg dipakai function buildGridLayer(cfg, cx, cy, heartScale) { const list = []; const rowStart = -1.4 * heartScale + (cfg.rowOffset || 0); const rowEnd = 1.3 * heartScale; const colStart = -1.5 * heartScale + (cfg.colOffset || 0); const colEnd = 1.5 * heartScale; for (let y = rowStart; y <= rowEnd; y += cfg.rowStep) { for (let x = colStart; x <= colEnd; x += cfg.colStep) { const lx = x / heartScale; const ly = y / heartScale; if (pointInHeartPolygon(lx, ly)) { const jx = (Math.random() * 2 - 1) * cfg.jitter; const jy = (Math.random() * 2 - 1) * cfg.jitter * 0.5; list.push({ x: cx + x + jx, y: cy + y + jy, word: cfg.words[Math.floor(Math.random() * cfg.words.length)], bold: cfg.bold, baseSize: cfg.fontSize * (0.92 + Math.random() * 0.16), phase: Math.random() * Math.PI * 2, speed: Math.random() * 0.5 + 0.25, flickerAmt: Math.random() * 0.45 + 0.25, maxAlpha: cfg.maxAlpha[0] + Math.random() * (cfg.maxAlpha[1] - cfg.maxAlpha[0]) }); } } } return list; } function buildPoints() { if (!heartPolygon) buildHeartPolygon(); const cx = W / 2; const cy = H / 2 - H * 0.05; const heartScale = Math.min(W, H) * 0.42; // lapisan besar & tebal: LOVE YOU / Love You, jarang, terang const coarse = buildGridLayer({ rowStep: 20 * scale, colStep: 56 * scale, fontSize: 12.5 * scale, words: ['LOVE YOU', 'Love You'], bold: true, maxAlpha: [0.8, 1.0], jitter: 4 * scale }, cx, cy, heartScale); // lapisan kecil pengisi: love you / Love You, rapat, agak redup, offset setengah sel const fine = buildGridLayer({ rowStep: 12 * scale, colStep: 37 * scale, fontSize: 7.5 * scale, words: ['love you', 'Love You'], bold: false, maxAlpha: [0.3, 0.6], jitter: 3 * scale, rowOffset: 6 * scale, colOffset: 18.5 * scale }, cx, cy, heartScale); particles = coarse.concat(fine); } function resize() { W = window.innerWidth; H = window.innerHeight; canvas.width = W * devicePixelRatio; canvas.height = H * devicePixelRatio; canvas.style.width = W + 'px'; canvas.style.height = H + 'px'; ctx.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0); scale = Math.min(W, H) / 480; buildPoints(); } let t0 = performance.now(); let buildStart = performance.now(); const BUILD_DURATION = 3800; // total waktu sampai hati penuh terbentuk (ms) let appearDelay = 0; function assignAppearOrder() { const order = [...particles.keys()]; for (let i = order.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [order[i], order[j]] = [order[j], order[i]]; } order.forEach((particleIdx, orderIdx) => { particles[particleIdx].appearAt = orderIdx; }); appearDelay = particles.length > 0 ? BUILD_DURATION / particles.length : 0; } function draw(now) { const time = (now - t0) / 1000; const buildElapsed = now - buildStart; ctx.clearRect(0, 0, W, H); ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; for (const p of particles) { const revealTime = p.appearAt * appearDelay; if (buildElapsed < revealTime) continue; // fade-in halus (tanpa efek pantul/pop), sesuai video: alpha naik pelan lalu berkedip const fadeProgress = Math.min(1, (buildElapsed - revealTime) / 500); const flicker = 0.5 + 0.5 * Math.sin(time * p.speed + p.phase); const alpha = p.maxAlpha * (1 - p.flickerAmt + p.flickerAmt * flicker) * fadeProgress; const fontSize = p.baseSize; ctx.save(); ctx.translate(p.x, p.y); ctx.font = `${p.bold ? '700' : '500'} ${fontSize}px 'Segoe UI', Arial, sans-serif`; ctx.shadowColor = `rgba(255, 45, 130, ${Math.min(1, alpha + 0.25)})`; ctx.shadowBlur = fontSize * (p.bold ? 1.1 : 0.7); ctx.fillStyle = `rgba(255, 160, 205, ${alpha})`; ctx.fillText(p.word, 0, 0); if (alpha > 0.7) { ctx.shadowBlur = fontSize * 1.5; ctx.fillStyle = `rgba(255, 225, 238, ${(alpha - 0.7) * 2})`; ctx.fillText(p.word, 0, 0); } ctx.restore(); } requestAnimationFrame(draw); } window.addEventListener('resize', () => { resize(); assignAppearOrder(); buildStart = performance.now(); }); canvas.addEventListener('click', () => { buildPoints(); assignAppearOrder(); buildStart = performance.now(); }); canvas.addEventListener('touchstart', () => { buildPoints(); assignAppearOrder(); buildStart = performance.now(); }); resize(); assignAppearOrder(); requestAnimationFrame(draw); </script> </body> </html>
Landing Page
This ad does not have a landing page available
Network Timeline
Performance Summary

1

Requests

1

Domains

7KB

Transfer Size

7KB

Content Size

134.0ms

Dom Content Loaded

224.0ms

First Paint

134.0ms

Load Time
Domain Breakdown
Transfer Size (bytes)
Loading...
Content Size (bytes)
Loading...
Header Size (bytes)
Loading...
Requests
Loading...
Timings (ms)
Loading...
Total Time
Loading...
Content Breakdown
Transfer Size (bytes)
Loading...
Content Size (bytes)
Loading...
Header Size (bytes)
Loading...
Requests
Loading...