Pulsar-Display / index.html
openfree's picture
Update index.html
3278d19 verified
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pulsar Mini โ€“ 16x16 ๋„ํŠธ ์ปฌ๋Ÿฌ</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
/* ๊ธฐ๋ณธ ๋ ˆ์ด์•„์›ƒ */
body {
margin: 0;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
background-color: black;
color: #e5e7eb;
font-family: sans-serif;
overflow: hidden;
}
.canvas-container {
position: relative;
overflow: hidden;
border-radius: 0.5rem;
border: 1px solid #374151;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
canvas {
display: block;
image-rendering: pixelated;
image-rendering: crisp-edges;
cursor: crosshair;
}
/* ์˜ค๋ฒ„๋ ˆ์ด UI */
#ui {
position: absolute;
top: 0.75rem;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 0.5rem;
z-index: 50;
user-select: none;
}
#ui button {
padding: 0.375rem 1rem;
border-radius: 0.25rem;
font-size: 0.875rem;
font-weight: 600;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
background-color: rgba(31, 41, 55, 0.7);
backdrop-filter: blur(4px);
}
#ui button:hover {
background-color: rgba(55, 65, 81, 0.7);
}
.hidden {
display: none;
}
</style>
</head>
<body>
<!-- ์ค‘์•™ ๋ฐฐ์น˜๋œ ์บ”๋ฒ„์Šค ์ปจํ…Œ์ด๋„ˆ -->
<div class="canvas-container">
<canvas id="canvas"></canvas>
<!-- UI ์˜ค๋ฒ„๋ ˆ์ด -->
<div id="ui">
<button id="play">โ–ถ ์žฌ์ƒ</button>
<button id="pause" class="hidden">โšโš ์ผ์‹œ์ •์ง€</button>
<button id="random">๐ŸŽฒ ๋žœ๋ค</button>
</div>
</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');
// ๋„ํŠธ ๊ทธ๋ฆฌ๋“œ ์„ค์ •
const GRID_SIZE = 16; // 16x16 ๋„ํŠธ
// ๋‹ค์–‘ํ•œ ์ˆ˜ํ•™ ๊ณต์‹์„ ํ™œ์šฉํ•œ ์ปฌ๋Ÿฌ ํŒจํ„ด ํ…œํ”Œ๋ฆฟ (R,G,B ๊ฐ๊ฐ์˜ ์‹)
const BASE_PATTERNS = [
// 1. ์‚ฌ์ธ/์ฝ”์‚ฌ์ธ ๊ฒฉ์ž
'(x,y,t)=>{const r=Math.sin((x+$S1)*$F1+t);const g=Math.sin((y+$S2)*$F2-t);const b=Math.cos((x+y)*$F3+t*0.5);return[(r+1)/2,(g+1)/2,(b+1)/2];}',
// 2. ์›ํ˜• ํŒŒ๋™
'(x,y,t)=>{const d=Math.hypot(x-0.5,y-0.5);return[0.5+0.5*Math.sin(d*$F1-t*2),0.5+0.5*Math.cos(d*$F2+t),0.5+0.5*Math.sin((x-y)*$F3+t)];}',
// 3. ๋ณต์†Œํ•จ์ˆ˜ ๊ธฐ๋ฐ˜ ํŒจํ„ด
'(x,y,t)=>{return[0.5+0.5*Math.sin((x*x-y*y)*$F1+t),0.5+0.5*Math.cos((x*y)*$F2-t),0.5+0.5*Math.sin((x+y)*$F3)];}',
// 4. ์ ˆ๋Œ€๊ฐ’ ์‚ฌ์ธ โ€“ ๋ฐ๊ณ  ์„ ๋ช…ํ•œ ์ƒ‰
'(x,y,t)=>{return[Math.abs(Math.sin($F1*x+t)),Math.abs(Math.sin($F2*y-t)),Math.abs(Math.sin(($F3)*(x+y)+t*0.3))];}',
// 5. ๊ทน์ขŒํ‘œ ๋ฌด์ง€๊ฐœ
'(x,y,t)=>{const a=Math.atan2(y-0.5,x-0.5);const r=(a/Math.PI+1)/2;return[r,0.5+0.5*Math.sin(t+$F1*r*10),1-r];}',
// 6. ๋กœ์ฆˆ(์žฅ๋ฏธ) ์ปค๋ธŒ ๊ธฐ๋ฐ˜
'(x,y,t)=>{const a=Math.atan2(y-0.5,x-0.5);const k=$K;return[0.5+0.5*Math.sin(k*a+t),0.5+0.5*Math.sin(k*a+t+2*Math.PI/3),0.5+0.5*Math.sin(k*a+t+4*Math.PI/3)];}',
// 7. ํŽ˜๋ฅด๋งˆ ๋‚˜์„  ๋…ธ์ด์ฆˆ
'(x,y,t)=>{const r=Math.hypot(x-0.5,y-0.5);const phi=r*10+$F1;return[0.5+0.5*Math.sin(phi+t),0.5+0.5*Math.cos(phi*1.3-t),0.5+0.5*Math.sin(phi*0.7+t*0.5)];}',
// 8. ์ƒˆ๋กœ์šด ํŒจํ„ด: ๊ฒฉ์ž ๋…ธ์ด์ฆˆ
'(x,y,t)=>{const nx=Math.floor(x*$F1)/($F1/2);const ny=Math.floor(y*$F2)/($F2/2);return[0.5+0.5*Math.sin(nx*$F3+t),0.5+0.5*Math.cos(ny*$F3-t),0.5+0.5*Math.sin((nx+ny)*$F3+t*0.7)];}',
// 9. ์ƒˆ๋กœ์šด ํŒจํ„ด: ํ”„๋ž™ํƒˆ ๊ธฐ๋ฐ˜
'(x,y,t)=>{let zx=3*(x-0.5),zy=3*(y-0.5),i=0,max=$IT;while(i<max&&zx*zx+zy*zy<4){const tmp=zx*zx-zy*zy;zy=2*zx*zy+0.5*Math.sin(t);zx=tmp+0.5*Math.cos(t);i++;}return[i/max,0.5+0.5*Math.sin(i/max*Math.PI+t),0.5+0.5*Math.cos(i/max*Math.PI*2-t)];}',
// 10. ์ƒˆ๋กœ์šด ํŒจํ„ด: ์ฒด์ปค๋ณด๋“œ ๋ณ€ํ˜•
'(x,y,t)=>{const cx=Math.floor(x*$F1);const cy=Math.floor(y*$F1);const even=(cx+cy)%2===0;const r=0.5+0.5*Math.sin(cx*cy/($F2)+t);const g=0.5+0.5*Math.cos((cx-cy)*$F3-t);const b=even?0.2+0.8*Math.sin(t*0.5):0.2+0.8*Math.cos(t*0.7);return[r,g,b];}',
// 11. ์ƒˆ๋กœ์šด ํŒจํ„ด: ๋ชจ์•„๋ ˆ ํŒจํ„ด
'(x,y,t)=>{const p1=Math.sin(x*$F1+t);const p2=Math.sin(y*$F2-t*0.7);const p3=Math.sin((x+y)*$F3+t*0.3);const moire=Math.sin(p1*p2*p3*10);return[0.5+0.5*moire,0.5+0.5*Math.sin(moire*Math.PI+t),0.5+0.5*Math.cos(moire*Math.PI-t)];}',
// 12. ์ƒˆ๋กœ์šด ํŒจํ„ด: ์ง„๋™ ๋ฌผ๊ฒฐ
'(x,y,t)=>{const wave1=Math.sin(y*$F1+x*$F2+t);const wave2=Math.sin(x*$F3-y*$F1-t*1.5);return[0.5+0.5*wave1,0.5+0.5*wave2,0.5+0.5*Math.sin(wave1*wave2*3+t)];}',
// 13. ์ƒˆ๋กœ์šด ํŒจํ„ด: ๋…ธ์ด์ฆˆ ํผ๋ฎคํ…Œ์ด์…˜
'(x,y,t)=>{const p=(x*$F1+y*$F2)%1;const q=(y*$F3-x*$F2+t)%1;return[p,q,0.5+0.5*Math.sin(p*q*Math.PI*2+t)];}',
// 14. ์ƒˆ๋กœ์šด ํŒจํ„ด: ๋„ํŠธ ๊ทธ๋ฆฌ๋“œ
'(x,y,t)=>{const gx=Math.floor(x*4)/4;const gy=Math.floor(y*4)/4;const r=0.5+0.5*Math.sin(gx*$F1+gy*$F2+t);const g=0.5+0.5*Math.cos(gx*$F3-gy*$F1-t);const b=0.5+0.5*Math.sin((gx-gy)*$F3*2+t*1.5);return[r,g,b];}',
];
function rand(min,max){return Math.random()*(max-min)+min;}
function pickRandomFormula(){
const tmpl=BASE_PATTERNS[Math.floor(Math.random()*BASE_PATTERNS.length)];
return tmpl
.replaceAll('$F1',rand(5,40).toFixed(2))
.replaceAll('$F2',rand(5,40).toFixed(2))
.replaceAll('$F3',rand(5,40).toFixed(2))
.replaceAll('$S1',Math.random().toFixed(2))
.replaceAll('$S2',Math.random().toFixed(2))
.replaceAll('$K',Math.floor(rand(3,12))) // ์ •์ˆ˜ ๊ฝƒ์žŽ ์ˆ˜
.replaceAll('$IT',Math.floor(rand(10,30))); // ํ”„๋ž™ํƒˆ ๋ฐ˜๋ณต ํšŸ์ˆ˜
}
let formulaSrc = pickRandomFormula();
let fn = compile(formulaSrc);
let playing = true;
let start = performance.now();
function resizeCanvas(){
// ๋ทฐํฌํŠธ ํฌ๊ธฐ์˜ 70%๋ฅผ ์ฐจ์ง€ํ•˜๋Š” ์ •์‚ฌ๊ฐํ˜•์œผ๋กœ ์„ค์ •
const size = Math.min(window.innerWidth, window.innerHeight) * 0.7;
// ์บ”๋ฒ„์Šค์˜ ์‹ค์ œ ํ•ด์ƒ๋„๋Š” 16x16
canvas.width = GRID_SIZE;
canvas.height = GRID_SIZE;
// ์บ”๋ฒ„์Šค ํ‘œ์‹œ ํฌ๊ธฐ๋Š” ํ™”๋ฉด์˜ 70%
canvas.style.width = `${size}px`;
canvas.style.height = `${size}px`;
// ์บ”๋ฒ„์Šค ์ปจํ…Œ์ด๋„ˆ๋„ ๊ฐ™์€ ํฌ๊ธฐ๋กœ ์„ค์ •
const container = document.querySelector('.canvas-container');
container.style.width = `${size}px`;
container.style.height = `${size}px`;
}
resizeCanvas();
window.addEventListener('resize',resizeCanvas);
function compile(src){
try{ return eval(src); }
catch(e){ console.error(e); return ()=>[0,0,0]; }
}
function clamp01(v){ return v<0?0:v>1?1:v; }
function draw(time){
const t = (time - start)/1000;
const w = canvas.width;
const h = canvas.height;
const img = ctx.createImageData(w,h);
const data = img.data;
let idx=0;
for(let y=0;y<h;y++){
for(let x=0;x<w;x++){
const c = fn(x/w,y/h,t,idx);
const r = clamp01(c[0])*255;
const g = clamp01(c[1])*255;
const b = clamp01(c[2])*255;
data[idx++] = r;
data[idx++] = g;
data[idx++] = b;
data[idx++] = 255;
}
}
ctx.putImageData(img,0,0);
if(playing) requestAnimationFrame(draw);
}
requestAnimationFrame(draw);
function randomize(){ formulaSrc = pickRandomFormula(); fn = compile(formulaSrc); start = performance.now(); }
// UI ์ด๋ฒคํŠธ
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>