Pulsar-Display / index.html
openfree's picture
Update index.html
df6a4c8 verified
raw
history blame
4.29 kB
<!DOCTYPE html>
<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>