Spaces:
Running
Running
<html lang="ko"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>Pulsar Mini β Touch Reactive</title> | |
<!-- Tailwind CDN: νλ‘ν νμ μ©, νμ μ λ‘컬 λΉλλ‘ κ΅μ²΄ --> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<style> | |
body { | |
@apply bg-gray-900 text-gray-100 flex flex-col items-center justify-center min-h-screen gap-6 p-4 select-none; | |
} | |
canvas { | |
image-rendering: pixelated; | |
@apply border border-gray-700 rounded shadow-lg; | |
} | |
</style> | |
</head> | |
<body> | |
<h1 class="text-3xl font-bold mb-2">Pulsar Mini</h1> | |
<p class="text-sm text-gray-400 mb-4 text-center">μΊλ²μ€λ₯Ό ννκ±°λ ν΄λ¦ν λλ§λ€ μλ‘μ΄ λ¬΄μμ ν¨ν΄μ΄ μ¬μλ©λλ€.</p> | |
<canvas id="canvas"></canvas> | |
<div class="flex gap-3"> | |
<button id="play" class="px-4 py-1.5 rounded bg-green-600 text-sm">βΆ μ¬μ</button> | |
<button id="pause" class="px-4 py-1.5 rounded bg-red-600 text-sm hidden">ββ μΌμμ μ§</button> | |
<button id="random" class="px-4 py-1.5 rounded bg-blue-600 text-sm">π² λλ€</button> | |
</div> | |
<script> | |
const DPR = window.devicePixelRatio || 1; | |
const canvas = document.getElementById('canvas'); | |
const ctx = canvas.getContext('2d'); | |
const playBtn = document.getElementById('play'); | |
const pauseBtn = document.getElementById('pause'); | |
const rndBtn = document.getElementById('random'); | |
// 10κ°μ§ μμ + λ§€ νΈμΆλ§λ€ λ§€κ°λ³μλ₯Ό λλ€μΌλ‘ μμ | |
const BASE_PATTERNS = [ | |
'(x,y,t)=>Math.sin((x+y+t)*$FREQ)', | |
'(x,y,t)=>Math.cos((x-y+t)*$FREQ)', | |
'(x,y,t)=>Math.sin(Math.hypot(x-0.5,y-0.5)*$FREQ - t*3)', | |
'(x,y,t)=>Math.sin(x*$FREQ+t)+Math.cos(y*$FREQ+t)', | |
'(x,y,t)=>Math.sin((x*$FREQ+y*$FREQ+t*2))*Math.cos((x*$FREQ-y*$FREQ+t))', | |
'(x,y,t)=>Math.sin((x+y)*$FREQ + t*5)*0.5+0.5', | |
'(x,y,t)=>Math.sin(Math.atan2(y-0.5,x-0.5)*$FREQ + t*2)', | |
'(x,y,t)=>((Math.sin(x*$FREQ)+Math.cos(y*$FREQ+t))*0.5)+0.5', | |
'(x,y,t)=>Math.sin(((x-0.5)**2+(y-0.5)**2)*$FREQ - t*4)', | |
'(x,y,t)=>Math.sin((x*x - y*y)*$FREQ + t*3)' | |
]; | |
function pickRandomFormula() { | |
const tmpl = BASE_PATTERNS[Math.floor(Math.random()*BASE_PATTERNS.length)]; | |
const freq = (Math.random()*30 + 10).toFixed(1); // 10~40 μ¬μ΄ μ£Όνμ | |
return tmpl.replaceAll('$FREQ', freq); | |
} | |
let formulaSrc = pickRandomFormula(); | |
let fn = compile(formulaSrc); | |
let playing = true; | |
let start = performance.now(); | |
function resizeCanvas() { | |
const size = Math.min(window.innerWidth, window.innerHeight) * 0.8; | |
canvas.style.width = size + 'px'; | |
canvas.style.height = size + 'px'; | |
canvas.width = size * DPR; | |
canvas.height = size * DPR; | |
ctx.scale(DPR, DPR); | |
} | |
resizeCanvas(); | |
window.addEventListener('resize', resizeCanvas); | |
function compile(src) { | |
try { | |
return eval(src); | |
} catch (e) { | |
console.error(e); | |
return () => 0; | |
} | |
} | |
function draw(time) { | |
const t = (time - start) / 1000; | |
const w = canvas.width / DPR; | |
const h = canvas.height / DPR; | |
const img = ctx.createImageData(w, h); | |
const data = img.data; | |
let i = 0; | |
for (let y = 0; y < h; y++) { | |
for (let x = 0; x < w; x++) { | |
const v = Math.max(0, Math.min(1, fn(x / w, y / h, t, i))); | |
const c = v * 255; | |
data[i++] = c; // R | |
data[i++] = c; // G | |
data[i++] = c; // B | |
data[i++] = 255; // A | |
} | |
} | |
ctx.putImageData(img, 0, 0); | |
if (playing) requestAnimationFrame(draw); | |
} | |
requestAnimationFrame(draw); | |
function randomize() { | |
formulaSrc = pickRandomFormula(); | |
fn = compile(formulaSrc); | |
start = performance.now(); | |
} | |
// λ²νΌ μ΄λ²€νΈ | |
playBtn.addEventListener('click', () => { | |
playing = true; | |
playBtn.classList.add('hidden'); | |
pauseBtn.classList.remove('hidden'); | |
start = performance.now(); | |
requestAnimationFrame(draw); | |
}); | |
pauseBtn.addEventListener('click', () => { | |
playing = false; | |
pauseBtn.classList.add('hidden'); | |
playBtn.classList.remove('hidden'); | |
}); | |
rndBtn.addEventListener('click', randomize); | |
canvas.addEventListener('pointerdown', randomize); // ν°μΉΒ·ν΄λ¦ λͺ¨λ λμ | |
</script> | |
</body> | |
</html> |