openfree commited on
Commit
df6a4c8
ยท
verified ยท
1 Parent(s): 9d4c549

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +106 -87
index.html CHANGED
@@ -1,114 +1,133 @@
1
  <!DOCTYPE html>
2
  <html lang="ko">
3
  <head>
4
- <meta charset="UTF-8">
5
- <title>Pulsar Mini</title>
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <!-- Tailwind CDN (๋น ๋ฅธ ํ”„๋กœํ† ํƒ€์ดํ•‘์šฉ) -->
 
 
8
  <script src="https://cdn.tailwindcss.com"></script>
9
  <style>
10
- /* custom utility ์กฐํ•ฉ */
11
- body { @apply bg-gray-900 text-gray-100 flex flex-col items-center py-6 gap-6; }
12
- canvas { image-rendering: pixelated; }
 
 
 
 
13
  </style>
14
  </head>
15
  <body>
16
- <h1 class="text-3xl font-bold">Pulsar Mini</h1>
 
17
 
18
- <div class="flex flex-wrap gap-4 max-w-4xl w-full justify-center">
19
- <!-- Canvas ์˜์—ญ -->
20
- <canvas id="canvas" width="320" height="320" class="border border-gray-700 rounded"></canvas>
21
 
22
- <!-- ์ปจํŠธ๋กค -->
23
- <div class="flex flex-col gap-3 w-80">
24
- <label class="text-sm font-mono">์ฝ”๋“œ:</label>
25
- <textarea
26
- id="code"
27
- rows="4"
28
- maxlength="300"
29
- class="w-full p-2 rounded bg-gray-800 font-mono text-xs resize-none outline-none"
30
- >(x,y,t)=>Math.sin(x*10+t)+Math.cos(y*10+t)</textarea>
31
-
32
- <div class="flex gap-2">
33
- <button id="play" class="px-3 py-1 rounded bg-green-600 text-sm">โ–ถ Play</button>
34
- <button id="pause" class="px-3 py-1 rounded bg-red-600 text-sm hidden">โšโš Pause</button>
35
- <button id="random" class="px-3 py-1 rounded bg-blue-600 text-sm">๐ŸŽฒ Random</button>
36
- </div>
37
-
38
- <p class="text-[11px] text-gray-400 leading-snug">
39
- ํ•จ์ˆ˜๋Š” <code>(x,y,t,i) =&gt; ๊ฐ’</code> ํ˜•์‹์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.<br>
40
- <code>x</code>, <code>y</code> ๋Š” 0~1 ์ขŒํ‘œ, <code>t</code> ๋Š” ์ดˆ, <code>i</code>๋Š” ํ”ฝ์…€ ์ธ๋ฑ์Šค์ž…๋‹ˆ๋‹ค.<br>
41
- ๋ฐ˜ํ™˜๊ฐ’์€ 0~1 ๋ฒ”์œ„๋กœ ํด๋žจํ”„๋˜์–ด ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๋ฐ๊ธฐ๋กœ ํ‘œํ˜„๋ฉ๋‹ˆ๋‹ค.
42
- </p>
43
- </div>
44
  </div>
45
 
46
- <script>
47
- const canvas = document.getElementById('canvas');
48
- const ctx = canvas.getContext('2d');
49
- const codeEl = document.getElementById('code');
50
- const playBtn = document.getElementById('play');
51
- const pauseBtn= document.getElementById('pause');
52
- const rndBtn = document.getElementById('random');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
- const EXAMPLES = [
55
- '(x,y,t)=>Math.sin((x+y+t)*6)',
56
- '(x,y,t)=>Math.sin(x*10+t)+Math.cos(y*10+t)',
57
- '(x,y,t)=>Math.sin(Math.hypot(x-0.5,y-0.5)*20-t*2)',
58
- ];
59
 
60
- let fn = compile(codeEl.value);
61
- let playing = false;
62
- let start = performance.now();
 
63
 
64
- function compile(src){
65
- try { return eval(src); }
66
- catch(e){ console.error(e); return ()=>0; }
67
- }
 
 
 
 
 
 
68
 
69
- function draw(time){
70
- const t = (time - start) / 1000;
71
- const w = canvas.width;
72
- const h = canvas.height;
73
- const img = ctx.getImageData(0,0,w,h);
74
- const data = img.data;
75
- for(let y=0; y<h; y++){
76
- for(let x=0; x<w; x++){
77
- const idx = (y*w + x) * 4;
78
- const v = Math.max(0, Math.min(1, fn(x/w, y/h, t, idx)));
79
- const c = v * 255;
80
- data[idx] = data[idx+1] = data[idx+2] = c;
81
- data[idx+3] = 255;
82
  }
83
  }
84
- ctx.putImageData(img,0,0);
85
- if(playing) requestAnimationFrame(draw);
86
- }
87
 
88
- draw(start);
 
 
 
89
 
90
- // event handlers
91
- playBtn.addEventListener('click', () => {
92
- playing = true;
93
- playBtn.classList.add('hidden');
94
- pauseBtn.classList.remove('hidden');
 
 
 
 
 
 
 
 
 
 
 
95
  requestAnimationFrame(draw);
96
- });
97
 
98
- pauseBtn.addEventListener('click', () => {
99
- playing = false;
100
- pauseBtn.classList.add('hidden');
101
- playBtn.classList.remove('hidden');
102
- });
 
 
 
 
 
 
 
 
 
103
 
104
- codeEl.addEventListener('input', () => {
105
- fn = compile(codeEl.value);
106
- });
 
 
107
 
108
- rndBtn.addEventListener('click', () => {
109
- codeEl.value = EXAMPLES[Math.floor(Math.random()*EXAMPLES.length)];
110
- fn = compile(codeEl.value);
111
- });
112
- </script>
113
  </body>
114
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="ko">
3
  <head>
4
+ <meta charset="UTF-8" />
 
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Pulsar Mini โ€“ Touch Reactive</title>
7
+
8
+ <!-- Tailwind CDN: ํ”„๋กœํ† ํƒ€์ž…์šฉ, ํ•„์š” ์‹œ ๋กœ์ปฌ ๋นŒ๋“œ๋กœ ๊ต์ฒด -->
9
  <script src="https://cdn.tailwindcss.com"></script>
10
  <style>
11
+ body {
12
+ @apply bg-gray-900 text-gray-100 flex flex-col items-center justify-center min-h-screen gap-6 p-4 select-none;
13
+ }
14
+ canvas {
15
+ image-rendering: pixelated;
16
+ @apply border border-gray-700 rounded shadow-lg;
17
+ }
18
  </style>
19
  </head>
20
  <body>
21
+ <h1 class="text-3xl font-bold mb-2">Pulsar Mini</h1>
22
+ <p class="text-sm text-gray-400 mb-4 text-center">์บ”๋ฒ„์Šค๋ฅผ ํƒญํ•˜๊ฑฐ๋‚˜ ํด๋ฆญํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๋ฌด์ž‘์œ„ ํŒจํ„ด์ด ์žฌ์ƒ๋ฉ๋‹ˆ๋‹ค.</p>
23
 
24
+ <canvas id="canvas"></canvas>
 
 
25
 
26
+ <div class="flex gap-3">
27
+ <button id="play" class="px-4 py-1.5 rounded bg-green-600 text-sm">โ–ถ ์žฌ์ƒ</button>
28
+ <button id="pause" class="px-4 py-1.5 rounded bg-red-600 text-sm hidden">โšโš ์ผ์‹œ์ •์ง€</button>
29
+ <button id="random" class="px-4 py-1.5 rounded bg-blue-600 text-sm">๐ŸŽฒ ๋žœ๋ค</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  </div>
31
 
32
+ <script>
33
+ const DPR = window.devicePixelRatio || 1;
34
+ const canvas = document.getElementById('canvas');
35
+ const ctx = canvas.getContext('2d');
36
+ const playBtn = document.getElementById('play');
37
+ const pauseBtn = document.getElementById('pause');
38
+ const rndBtn = document.getElementById('random');
39
+
40
+ // 10๊ฐ€์ง€ ์˜ˆ์ œ + ๋งค ํ˜ธ์ถœ๋งˆ๋‹ค ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋žœ๋ค์œผ๋กœ ์„ž์Œ
41
+ const BASE_PATTERNS = [
42
+ '(x,y,t)=>Math.sin((x+y+t)*$FREQ)',
43
+ '(x,y,t)=>Math.cos((x-y+t)*$FREQ)',
44
+ '(x,y,t)=>Math.sin(Math.hypot(x-0.5,y-0.5)*$FREQ - t*3)',
45
+ '(x,y,t)=>Math.sin(x*$FREQ+t)+Math.cos(y*$FREQ+t)',
46
+ '(x,y,t)=>Math.sin((x*$FREQ+y*$FREQ+t*2))*Math.cos((x*$FREQ-y*$FREQ+t))',
47
+ '(x,y,t)=>Math.sin((x+y)*$FREQ + t*5)*0.5+0.5',
48
+ '(x,y,t)=>Math.sin(Math.atan2(y-0.5,x-0.5)*$FREQ + t*2)',
49
+ '(x,y,t)=>((Math.sin(x*$FREQ)+Math.cos(y*$FREQ+t))*0.5)+0.5',
50
+ '(x,y,t)=>Math.sin(((x-0.5)**2+(y-0.5)**2)*$FREQ - t*4)',
51
+ '(x,y,t)=>Math.sin((x*x - y*y)*$FREQ + t*3)'
52
+ ];
53
 
54
+ function pickRandomFormula() {
55
+ const tmpl = BASE_PATTERNS[Math.floor(Math.random()*BASE_PATTERNS.length)];
56
+ const freq = (Math.random()*30 + 10).toFixed(1); // 10~40 ์‚ฌ์ด ์ฃผํŒŒ์ˆ˜
57
+ return tmpl.replaceAll('$FREQ', freq);
58
+ }
59
 
60
+ let formulaSrc = pickRandomFormula();
61
+ let fn = compile(formulaSrc);
62
+ let playing = true;
63
+ let start = performance.now();
64
 
65
+ function resizeCanvas() {
66
+ const size = Math.min(window.innerWidth, window.innerHeight) * 0.8;
67
+ canvas.style.width = size + 'px';
68
+ canvas.style.height = size + 'px';
69
+ canvas.width = size * DPR;
70
+ canvas.height = size * DPR;
71
+ ctx.scale(DPR, DPR);
72
+ }
73
+ resizeCanvas();
74
+ window.addEventListener('resize', resizeCanvas);
75
 
76
+ function compile(src) {
77
+ try {
78
+ return eval(src);
79
+ } catch (e) {
80
+ console.error(e);
81
+ return () => 0;
 
 
 
 
 
 
 
82
  }
83
  }
 
 
 
84
 
85
+ function draw(time) {
86
+ const t = (time - start) / 1000;
87
+ const w = canvas.width / DPR;
88
+ const h = canvas.height / DPR;
89
 
90
+ const img = ctx.createImageData(w, h);
91
+ const data = img.data;
92
+ let i = 0;
93
+ for (let y = 0; y < h; y++) {
94
+ for (let x = 0; x < w; x++) {
95
+ const v = Math.max(0, Math.min(1, fn(x / w, y / h, t, i)));
96
+ const c = v * 255;
97
+ data[i++] = c; // R
98
+ data[i++] = c; // G
99
+ data[i++] = c; // B
100
+ data[i++] = 255; // A
101
+ }
102
+ }
103
+ ctx.putImageData(img, 0, 0);
104
+ if (playing) requestAnimationFrame(draw);
105
+ }
106
  requestAnimationFrame(draw);
 
107
 
108
+ function randomize() {
109
+ formulaSrc = pickRandomFormula();
110
+ fn = compile(formulaSrc);
111
+ start = performance.now();
112
+ }
113
+
114
+ // ๋ฒ„ํŠผ ์ด๋ฒคํŠธ
115
+ playBtn.addEventListener('click', () => {
116
+ playing = true;
117
+ playBtn.classList.add('hidden');
118
+ pauseBtn.classList.remove('hidden');
119
+ start = performance.now();
120
+ requestAnimationFrame(draw);
121
+ });
122
 
123
+ pauseBtn.addEventListener('click', () => {
124
+ playing = false;
125
+ pauseBtn.classList.add('hidden');
126
+ playBtn.classList.remove('hidden');
127
+ });
128
 
129
+ rndBtn.addEventListener('click', randomize);
130
+ canvas.addEventListener('pointerdown', randomize); // ํ„ฐ์น˜ยทํด๋ฆญ ๋ชจ๋‘ ๋Œ€์‘
131
+ </script>
 
 
132
  </body>
133
  </html>