greerben0 commited on
Commit
97d768e
1 Parent(s): fe530b9

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +758 -19
index.html CHANGED
@@ -1,19 +1,758 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Animated Fractal Shader Playground</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ font-family: 'Monaco', monospace;
13
+ }
14
+
15
+ body {
16
+ background: #1a1a1a;
17
+ color: #fff;
18
+ display: flex;
19
+ min-height: 100vh;
20
+ }
21
+
22
+ .controls-panel {
23
+ width: 350px;
24
+ background: #252525;
25
+ padding: 20px;
26
+ overflow-y: auto;
27
+ }
28
+
29
+ .main-panel {
30
+ flex: 1;
31
+ display: flex;
32
+ flex-direction: column;
33
+ }
34
+
35
+ canvas {
36
+ width: 100%;
37
+ flex: 1;
38
+ background: #000;
39
+ }
40
+
41
+ .control-group {
42
+ margin-bottom: 20px;
43
+ border: 1px solid #333;
44
+ padding: 10px;
45
+ border-radius: 4px;
46
+ background: #1f1f1f;
47
+ }
48
+
49
+ .control-group h3 {
50
+ margin-bottom: 10px;
51
+ color: #0f0;
52
+ font-size: 14px;
53
+ text-transform: uppercase;
54
+ }
55
+
56
+ .control-item {
57
+ margin-bottom: 12px;
58
+ }
59
+
60
+ label {
61
+ display: block;
62
+ font-size: 12px;
63
+ color: #aaa;
64
+ margin-bottom: 4px;
65
+ }
66
+
67
+ input[type="range"] {
68
+ width: 100%;
69
+ background: #333;
70
+ height: 6px;
71
+ -webkit-appearance: none;
72
+ border-radius: 3px;
73
+ }
74
+
75
+ input[type="range"]::-webkit-slider-thumb {
76
+ -webkit-appearance: none;
77
+ width: 15px;
78
+ height: 15px;
79
+ background: #0f0;
80
+ border-radius: 50%;
81
+ cursor: pointer;
82
+ }
83
+
84
+ input[type="color"] {
85
+ width: 100%;
86
+ height: 30px;
87
+ border: none;
88
+ background: #333;
89
+ cursor: pointer;
90
+ }
91
+
92
+ select, textarea {
93
+ width: 100%;
94
+ background: #333;
95
+ color: #fff;
96
+ padding: 5px;
97
+ border: 1px solid #444;
98
+ border-radius: 3px;
99
+ }
100
+
101
+ textarea {
102
+ height: 100px;
103
+ font-size: 12px;
104
+ resize: vertical;
105
+ font-family: monospace;
106
+ }
107
+
108
+ .preset-btn {
109
+ padding: 8px;
110
+ background: #2a2a2a;
111
+ border: 1px solid #444;
112
+ color: #0f0;
113
+ width: 100%;
114
+ margin: 2px 0;
115
+ cursor: pointer;
116
+ transition: all 0.3s;
117
+ border-radius: 3px;
118
+ }
119
+
120
+ .preset-btn:hover {
121
+ background: #333;
122
+ border-color: #0f0;
123
+ }
124
+
125
+ .coordinates {
126
+ position: fixed;
127
+ bottom: 10px;
128
+ right: 10px;
129
+ background: rgba(0,0,0,0.8);
130
+ padding: 8px 12px;
131
+ border-radius: 4px;
132
+ font-size: 12px;
133
+ color: #0f0;
134
+ border: 1px solid #333;
135
+ }
136
+
137
+ .checkbox-group {
138
+ display: flex;
139
+ gap: 10px;
140
+ align-items: center;
141
+ }
142
+
143
+ .checkbox-group input[type="checkbox"] {
144
+ width: 16px;
145
+ height: 16px;
146
+ cursor: pointer;
147
+ }
148
+
149
+ .animation-controls {
150
+ display: flex;
151
+ gap: 10px;
152
+ margin-top: 10px;
153
+ }
154
+
155
+ .animation-btn {
156
+ flex: 1;
157
+ padding: 8px;
158
+ background: #333;
159
+ border: 1px solid #444;
160
+ color: #0f0;
161
+ cursor: pointer;
162
+ border-radius: 3px;
163
+ transition: all 0.3s;
164
+ }
165
+
166
+ .animation-btn:hover {
167
+ background: #444;
168
+ border-color: #0f0;
169
+ }
170
+
171
+ .stats {
172
+ position: fixed;
173
+ top: 10px;
174
+ right: 10px;
175
+ background: rgba(0,0,0,0.8);
176
+ padding: 8px;
177
+ border-radius: 4px;
178
+ font-size: 12px;
179
+ color: #0f0;
180
+ }
181
+ </style>
182
+ </head>
183
+ <body>
184
+ <div class="controls-panel" id="controls">
185
+ <div class="control-group">
186
+ <h3>Fractal Type</h3>
187
+ <select id="fractalType" onchange="updateShader()">
188
+ <option value="mandelbrot">Mandelbrot Set</option>
189
+ <option value="julia">Julia Set</option>
190
+ <option value="burningShip">Burning Ship</option>
191
+ <option value="tricorn">Tricorn</option>
192
+ <option value="custom">Custom Function</option>
193
+ </select>
194
+ </div>
195
+
196
+ <div class="control-group" id="customFunctionGroup" style="display:none">
197
+ <h3>Custom Function</h3>
198
+ <textarea id="customFunction">z = vec2(
199
+ z.x * z.x - z.y * z.y,
200
+ 2.0 * z.x * z.y
201
+ ) + c;</textarea>
202
+ </div>
203
+
204
+ <div class="control-group">
205
+ <h3>Animation</h3>
206
+ <div class="control-item">
207
+ <label>Animation Speed</label>
208
+ <input type="range" id="animSpeed" min="0" max="2" step="0.01" value="0.5">
209
+ </div>
210
+ <div class="control-item">
211
+ <label>Motion Pattern</label>
212
+ <select id="motionPattern">
213
+ <option value="orbit">Orbital</option>
214
+ <option value="wave">Wave</option>
215
+ <option value="spiral">Spiral</option>
216
+ <option value="bounce">Bounce</option>
217
+ </select>
218
+ </div>
219
+ <div class="checkbox-group">
220
+ <input type="checkbox" id="animateColors" checked>
221
+ <label>Animate Colors</label>
222
+ </div>
223
+ <div class="animation-controls">
224
+ <button class="animation-btn" id="playPause">Pause</button>
225
+ <button class="animation-btn" id="reset">Reset</button>
226
+ </div>
227
+ </div>
228
+
229
+ <div class="control-group">
230
+ <h3>Navigation</h3>
231
+ <div class="control-item">
232
+ <label>Zoom</label>
233
+ <input type="range" id="zoom" min="-2" max="2" step="0.01" value="0">
234
+ </div>
235
+ <div class="control-item">
236
+ <label>X Position</label>
237
+ <input type="range" id="xPos" min="-2" max="2" step="0.01" value="0">
238
+ </div>
239
+ <div class="control-item">
240
+ <label>Y Position</label>
241
+ <input type="range" id="yPos" min="-2" max="2" step="0.01" value="0">
242
+ </div>
243
+ <div class="control-item">
244
+ <label>Rotation</label>
245
+ <input type="range" id="rotation" min="0" max="360" step="1" value="0">
246
+ </div>
247
+ </div>
248
+
249
+ <div class="control-group">
250
+ <h3>Iteration Settings</h3>
251
+ <div class="control-item">
252
+ <label>Max Iterations</label>
253
+ <input type="range" id="maxIterations" min="10" max="1000" step="10" value="100">
254
+ </div>
255
+ <div class="control-item">
256
+ <label>Escape Radius</label>
257
+ <input type="range" id="escapeRadius" min="2" max="20" step="0.5" value="4">
258
+ </div>
259
+ </div>
260
+
261
+ <div class="control-group">
262
+ <h3>Color Settings</h3>
263
+ <div class="control-item">
264
+ <label>Color Scheme</label>
265
+ <select id="colorScheme">
266
+ <option value="smooth">Smooth</option>
267
+ <option value="bands">Color Bands</option>
268
+ <option value="hsv">HSV Cycle</option>
269
+ <option value="rainbow">Rainbow</option>
270
+ <option value="fire">Fire</option>
271
+ </select>
272
+ </div>
273
+ <div class="control-item">
274
+ <label>Primary Color</label>
275
+ <input type="color" id="color1" value="#00ff00">
276
+ </div>
277
+ <div class="control-item">
278
+ <label>Secondary Color</label>
279
+ <input type="color" id="color2" value="#0000ff">
280
+ </div>
281
+ <div class="control-item">
282
+ <label>Color Bands</label>
283
+ <input type="range" id="colorBands" min="1" max="20" step="1" value="5">
284
+ </div>
285
+ <div class="control-item">
286
+ <label>Color Intensity</label>
287
+ <input type="range" id="colorIntensity" min="0.1" max="2" step="0.1" value="1">
288
+ </div>
289
+ <div class="control-item">
290
+ <label>Color Speed</label>
291
+ <input type="range" id="colorSpeed" min="0" max="2" step="0.1" value="1">
292
+ </div>
293
+ </div>
294
+
295
+ <div class="control-group">
296
+ <h3>Effects</h3>
297
+ <div class="control-item">
298
+ <label>Distortion</label>
299
+ <input type="range" id="distortion" min="0" max="1" step="0.01" value="0">
300
+ </div>
301
+ <div class="control-item">
302
+ <label>Glow Intensity</label>
303
+ <input type="range" id="glow" min="0" max="1" step="0.01" value="0.2">
304
+ </div>
305
+ <div class="checkbox-group">
306
+ <input type="checkbox" id="symmetry">
307
+ <label>Enable Symmetry</label>
308
+ </div>
309
+ </div>
310
+
311
+ <div class="control-group">
312
+ <h3>Presets</h3>
313
+ <button class="preset-btn" onclick="loadPreset('classic')">Classic</button>
314
+ <button class="preset-btn" onclick="loadPreset('psychedelic')">Psychedelic</button>
315
+ <button class="preset-btn" onclick="loadPreset('cosmic')">Cosmic</button>
316
+ <button class="preset-btn" onclick="loadPreset('fire')">Fire Dance</button>
317
+ <button class="preset-btn" onclick="loadPreset('vortex')">Vortex</button>
318
+ </div>
319
+ </div>
320
+
321
+ <div class="main-panel">
322
+ <canvas id="canvas"></canvas>
323
+ <div class="coordinates" id="coords"></div>
324
+ <div class="stats" id="stats"></div>
325
+ </div>
326
+
327
+ <script>
328
+ // Main shader code with all supported features
329
+ const fragmentShaderSource = `
330
+ precision highp float;
331
+
332
+ uniform vec2 resolution;
333
+ uniform vec2 position;
334
+ uniform float zoom;
335
+ uniform float rotation;
336
+ uniform float time;
337
+ uniform int maxIterations;
338
+ uniform float escapeRadius;
339
+ uniform vec3 color1;
340
+ uniform vec3 color2;
341
+ uniform float colorIntensity;
342
+ uniform int colorScheme;
343
+ uniform float colorBands;
344
+ uniform float distortion;
345
+ uniform float glow;
346
+ uniform bool symmetry;
347
+ uniform int fractalType;
348
+ uniform int motionPattern;
349
+ uniform float animSpeed;
350
+ uniform bool animateColors;
351
+ uniform float colorSpeed;
352
+
353
+ vec2 rotate(vec2 p, float angle) {
354
+ float c = cos(angle);
355
+ float s = sin(angle);
356
+ return vec2(
357
+ p.x * c - p.y * s,
358
+ p.x * s + p.y * c
359
+ );
360
+ }
361
+
362
+ vec2 complexMul(vec2 a, vec2 b) {
363
+ return vec2(
364
+ a.x * b.x - a.y * b.y,
365
+ a.x * b.y + a.y * b.x
366
+ );
367
+ }
368
+
369
+ vec2 complexDiv(vec2 a, vec2 b) {
370
+ float denom = b.x * b.x + b.y * b.y;
371
+ return vec2(
372
+ (a.x * b.x + a.y * b.y) / denom,
373
+ (a.y * b.x - a.x * b.y) / denom
374
+ );
375
+ }
376
+
377
+ vec3 hsv2rgb(vec3 c) {
378
+ vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
379
+ vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
380
+ return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
381
+ }
382
+
383
+ vec2 getMotionOffset() {
384
+ if(motionPattern == 0) { // Orbit
385
+ return vec2(
386
+ cos(time * animSpeed),
387
+ sin(time * animSpeed)
388
+ ) * 0.1;
389
+ } else if(motionPattern == 1) { // Wave
390
+ return vec2(
391
+ sin(time * animSpeed) * 0.1,
392
+ cos(time * animSpeed * 2.0) * 0.1
393
+ );
394
+ } else if(motionPattern == 2) { // Spiral
395
+ float t = time * animSpeed;
396
+ return vec2(
397
+ cos(t) * t * 0.01,
398
+ sin(t) * t * 0.01
399
+ );
400
+ } else { // Bounce
401
+ return vec2(
402
+ abs(sin(time * animSpeed)),
403
+ abs(cos(time * animSpeed))
404
+ ) * 0.1;
405
+ }
406
+ }
407
+
408
+ vec3 getColor(float iter, vec2 z) {
409
+ float smoothed = iter + 1.0 - log(log(length(z))) / log(2.0);
410
+ float normalized = smoothed / float(maxIterations);
411
+
412
+ if(animateColors) {
413
+ normalized = mod(normalized + time * colorSpeed, 1.0);
414
+ }
415
+
416
+ if(colorScheme == 0) { // Smooth
417
+ return mix(color1, color2, normalized) * colorIntensity;
418
+ } else if(colorScheme == 1) { // Bands
419
+ return mix(color1, color2, mod(normalized * colorBands, 1.0)) * colorIntensity;
420
+ } else if(colorScheme == 2) { // HSV
421
+ return hsv2rgb(vec3(normalized, 1.0, 1.0)) * colorIntensity;
422
+ } else if(colorScheme == 3) { // Rainbow
423
+ return vec3(
424
+ 0.5 + 0.5 * sin(normalized * 6.28318),
425
+ 0.5 + 0.5 * sin(normalized * 6.28318 + 2.09439),
426
+ 0.5 + 0.5 * sin(normalized * 6.28318 + 4.18879)
427
+ ) * colorIntensity;
428
+ } else { // Fire
429
+ return vec3(
430
+ normalized * 1.5,
431
+ pow(normalized, 2.0),
432
+ pow(normalized, 4.0)
433
+ ) * colorIntensity;
434
+ }
435
+ }
436
+
437
+ void main() {
438
+ vec2 uv = (gl_FragCoord.xy - 0.5 * resolution.xy) / min(resolution.x, resolution.y);
439
+ uv = rotate(uv, rotation * 0.0174533);
440
+
441
+ vec2 offset = getMotionOffset();
442
+ vec2 c = uv * pow(2.0, zoom) + position + offset;
443
+
444
+ if(symmetry) {
445
+ c = abs(c);
446
+ }
447
+
448
+ vec2 z = fractalType == 1 ? uv : c;
449
+ vec2 juliaC = fractalType == 1 ? vec2(0.285, 0.01) + offset : c;
450
+
451
+ float iter = 0.0;
452
+
453
+ for(int i = 0; i < 1000; i++) {
454
+ if(i >= maxIterations) break;
455
+
456
+ if(fractalType == 0 || fractalType == 1) {
457
+ z = complexMul(z, z) + juliaC;
458
+ } else if(fractalType == 2) {
459
+ z = complexMul(abs(z), abs(z)) + c;
460
+ } else if(fractalType == 3) {
461
+ z = vec2(z.x, -z.y);
462
+ z = complexMul(z, z) + c;
463
+ }
464
+
465
+ // Apply distortion
466
+ z += vec2(sin(z.y * distortion), cos(z.x * distortion)) * distortion;
467
+
468
+ if(length(z) > escapeRadius) {
469
+ vec3 color = getColor(iter, z);
470
+
471
+ // Apply glow effect
472
+ float glowFactor = 1.0 + glow * (1.0 - iter / float(maxIterations));
473
+ color *= glowFactor;
474
+
475
+ gl_FragColor = vec4(color, 1.0);
476
+ return;
477
+ }
478
+ iter += 1.0;
479
+ }
480
+
481
+ gl_FragColor = vec4(vec3(0.0), 1.0);
482
+ }
483
+ `;
484
+
485
+ let gl, program;
486
+ let isAnimating = true;
487
+ let startTime = Date.now();
488
+ let frameCount = 0;
489
+ let lastFpsUpdate = startTime;
490
+ const uniforms = {};
491
+
492
+ function initGL() {
493
+ const canvas = document.querySelector('canvas');
494
+ canvas.width = canvas.clientWidth;
495
+ canvas.height = canvas.clientHeight;
496
+
497
+ gl = canvas.getContext('webgl');
498
+ if (!gl) {
499
+ alert('WebGL not supported');
500
+ return;
501
+ }
502
+
503
+ // Create and compile shaders
504
+ const vertexShader = gl.createShader(gl.VERTEX_SHADER);
505
+ gl.shaderSource(vertexShader, `
506
+ attribute vec2 position;
507
+ void main() {
508
+ gl_Position = vec4(position, 0.0, 1.0);
509
+ }
510
+ `);
511
+ gl.compileShader(vertexShader);
512
+
513
+ const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
514
+ gl.shaderSource(fragmentShader, fragmentShaderSource);
515
+ gl.compileShader(fragmentShader);
516
+
517
+ // Create and link program
518
+ program = gl.createProgram();
519
+ gl.attachShader(program, vertexShader);
520
+ gl.attachShader(program, fragmentShader);
521
+ gl.linkProgram(program);
522
+
523
+ // Set up geometry
524
+ const vertices = new Float32Array([-1,-1, 1,-1, -1,1, 1,1]);
525
+ const buffer = gl.createBuffer();
526
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
527
+ gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
528
+
529
+ const positionLocation = gl.getAttribLocation(program, 'position');
530
+ gl.enableVertexAttribArray(positionLocation);
531
+ gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
532
+
533
+ // Store uniform locations
534
+ const uniformNames = [
535
+ 'resolution', 'position', 'zoom', 'rotation', 'time',
536
+ 'maxIterations', 'escapeRadius', 'color1', 'color2',
537
+ 'colorIntensity', 'colorScheme', 'colorBands', 'distortion',
538
+ 'glow', 'symmetry', 'fractalType', 'motionPattern',
539
+ 'animSpeed', 'animateColors', 'colorSpeed'
540
+ ];
541
+
542
+ uniformNames.forEach(name => {
543
+ uniforms[name] = gl.getUniformLocation(program, name);
544
+ });
545
+ }
546
+
547
+ function hexToRGB(hex) {
548
+ const r = parseInt(hex.substr(1,2), 16) / 255;
549
+ const g = parseInt(hex.substr(3,2), 16) / 255;
550
+ const b = parseInt(hex.substr(5,2), 16) / 255;
551
+ return [r, g, b];
552
+ }
553
+
554
+ function updateShader() {
555
+ if (!isAnimating) return;
556
+
557
+ gl.useProgram(program);
558
+
559
+ // Update uniforms
560
+ const time = (Date.now() - startTime) / 1000;
561
+ gl.uniform2f(uniforms.resolution, canvas.width, canvas.height);
562
+ gl.uniform2f(uniforms.position,
563
+ parseFloat(document.getElementById('xPos').value),
564
+ parseFloat(document.getElementById('yPos').value)
565
+ );
566
+ gl.uniform1f(uniforms.zoom, parseFloat(document.getElementById('zoom').value));
567
+ gl.uniform1f(uniforms.rotation, parseFloat(document.getElementById('rotation').value));
568
+ gl.uniform1f(uniforms.time, time);
569
+ gl.uniform1i(uniforms.maxIterations, parseInt(document.getElementById('maxIterations').value));
570
+ gl.uniform1f(uniforms.escapeRadius, parseFloat(document.getElementById('escapeRadius').value));
571
+
572
+ const color1 = hexToRGB(document.getElementById('color1').value);
573
+ const color2 = hexToRGB(document.getElementById('color2').value);
574
+ gl.uniform3f(uniforms.color1, color1[0], color1[1], color1[2]);
575
+ gl.uniform3f(uniforms.color2, color2[0], color2[1], color2[2]);
576
+
577
+ gl.uniform1f(uniforms.colorIntensity, parseFloat(document.getElementById('colorIntensity').value));
578
+ gl.uniform1i(uniforms.colorScheme, document.getElementById('colorScheme').selectedIndex);
579
+ gl.uniform1f(uniforms.colorBands, parseFloat(document.getElementById('colorBands').value));
580
+ gl.uniform1f(uniforms.distortion, parseFloat(document.getElementById('distortion').value));
581
+ gl.uniform1f(uniforms.glow, parseFloat(document.getElementById('glow').value));
582
+ gl.uniform1i(uniforms.fractalType, document.getElementById('fractalType').selectedIndex);
583
+ gl.uniform1i(uniforms.motionPattern, document.getElementById('motionPattern').selectedIndex);
584
+ gl.uniform1f(uniforms.animSpeed, parseFloat(document.getElementById('animSpeed').value));
585
+ gl.uniform1f(uniforms.colorSpeed, parseFloat(document.getElementById('colorSpeed').value));
586
+
587
+ gl.uniform1i(uniforms.symmetry, document.getElementById('symmetry').checked);
588
+ gl.uniform1i(uniforms.animateColors, document.getElementById('animateColors').checked);
589
+
590
+ // Draw
591
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
592
+
593
+ // Update FPS counter
594
+ frameCount++;
595
+ const currentTime = Date.now();
596
+ if (currentTime - lastFpsUpdate >= 1000) {
597
+ const fps = Math.round((frameCount * 1000) / (currentTime - lastFpsUpdate));
598
+ document.getElementById('stats').textContent = `FPS: ${fps}`;
599
+ frameCount = 0;
600
+ lastFpsUpdate = currentTime;
601
+ }
602
+
603
+ // Request next frame
604
+ if (isAnimating) {
605
+ requestAnimationFrame(updateShader);
606
+ }
607
+ }
608
+
609
+ const presets = {
610
+ classic: {
611
+ fractalType: 'mandelbrot',
612
+ zoom: 0,
613
+ colorScheme: 'smooth',
614
+ animSpeed: 0.5,
615
+ motionPattern: 'orbit',
616
+ distortion: 0,
617
+ glow: 0.2,
618
+ colorIntensity: 1
619
+ },
620
+ psychedelic: {
621
+ fractalType: 'julia',
622
+ zoom: 0.5,
623
+ colorScheme: 'rainbow',
624
+ animSpeed: 1,
625
+ motionPattern: 'spiral',
626
+ distortion: 0.3,
627
+ glow: 0.5,
628
+ colorIntensity: 1.5
629
+ },
630
+ cosmic: {
631
+ fractalType: 'tricorn',
632
+ zoom: 0.2,
633
+ colorScheme: 'hsv',
634
+ animSpeed: 0.8,
635
+ motionPattern: 'wave',
636
+ distortion: 0.1,
637
+ glow: 0.4,
638
+ colorIntensity: 1.2
639
+ },
640
+ fire: {
641
+ fractalType: 'burningShip',
642
+ zoom: 0.3,
643
+ colorScheme: 'fire',
644
+ animSpeed: 1.2,
645
+ motionPattern: 'bounce',
646
+ distortion: 0.2,
647
+ glow: 0.6,
648
+ colorIntensity: 1.8
649
+ },
650
+ vortex: {
651
+ fractalType: 'julia',
652
+ zoom: 0.1,
653
+ colorScheme: 'bands',
654
+ animSpeed: 1.5,
655
+ motionPattern: 'spiral',
656
+ distortion: 0.4,
657
+ glow: 0.3,
658
+ colorIntensity: 1.4
659
+ }
660
+ };
661
+
662
+ function loadPreset(name) {
663
+ const preset = presets[name];
664
+ Object.keys(preset).forEach(key => {
665
+ const element = document.getElementById(key);
666
+ if (element) {
667
+ element.value = preset[key];
668
+ }
669
+ });
670
+ }
671
+
672
+ // Event Listeners
673
+ document.getElementById('playPause').addEventListener('click', function() {
674
+ isAnimating = !isAnimating;
675
+ this.textContent = isAnimating ? 'Pause' : 'Play';
676
+ if (isAnimating) {
677
+ startTime = Date.now() - (parseFloat(document.getElementById('time').value) || 0) * 1000;
678
+ updateShader();
679
+ }
680
+ });
681
+
682
+ document.getElementById('reset').addEventListener('click', function() {
683
+ startTime = Date.now();
684
+ if (!isAnimating) {
685
+ isAnimating = true;
686
+ document.getElementById('playPause').textContent = 'Pause';
687
+ updateShader();
688
+ }
689
+ });
690
+
691
+ document.getElementById('fractalType').addEventListener('change', function() {
692
+ document.getElementById('customFunctionGroup').style.display =
693
+ this.value === 'custom' ? 'block' : 'none';
694
+ });
695
+
696
+ // Mouse interaction
697
+ let isDragging = false;
698
+ let lastMouseX, lastMouseY;
699
+
700
+ canvas.addEventListener('mousedown', (e) => {
701
+ isDragging = true;
702
+ lastMouseX = e.clientX;
703
+ lastMouseY = e.clientY;
704
+ });
705
+
706
+ canvas.addEventListener('mousemove', (e) => {
707
+ if (!isDragging) {
708
+ const rect = canvas.getBoundingClientRect();
709
+ const x = (e.clientX - rect.left) / canvas.width;
710
+ const y = (e.clientY - rect.top) / canvas.height;
711
+ const zoom = Math.pow(2.0, parseFloat(document.getElementById('zoom').value));
712
+ const xPos = parseFloat(document.getElementById('xPos').value);
713
+ const yPos = parseFloat(document.getElementById('yPos').value);
714
+
715
+ const coordX = (x - 0.5) * 4 / zoom + xPos;
716
+ const coordY = (0.5 - y) * 4 / zoom + yPos;
717
+
718
+ document.getElementById('coords').textContent =
719
+ `x: ${coordX.toFixed(6)}, y: ${coordY.toFixed(6)}`;
720
+ return;
721
+ }
722
+
723
+ const deltaX = (e.clientX - lastMouseX) / canvas.width;
724
+ const deltaY = (e.clientY - lastMouseY) / canvas.height;
725
+ const zoom = Math.pow(2.0, parseFloat(document.getElementById('zoom').value));
726
+
727
+ document.getElementById('xPos').value =
728
+ parseFloat(document.getElementById('xPos').value) - deltaX * 4 / zoom;
729
+ document.getElementById('yPos').value =
730
+ parseFloat(document.getElementById('yPos').value) + deltaY * 4 / zoom;
731
+
732
+ lastMouseX = e.clientX;
733
+ lastMouseY = e.clientY;
734
+ });
735
+
736
+ canvas.addEventListener('mouseup', () => isDragging = false);
737
+ canvas.addEventListener('mouseleave', () => isDragging = false);
738
+
739
+ canvas.addEventListener('wheel', (e) => {
740
+ e.preventDefault();
741
+ const delta = e.deltaY > 0 ? -0.1 : 0.1;
742
+ document.getElementById('zoom').value =
743
+ parseFloat(document.getElementById('zoom').value) + delta;
744
+ });
745
+
746
+ // Initialize
747
+ initGL();
748
+ updateShader();
749
+
750
+ // Handle window resize
751
+ window.addEventListener('resize', () => {
752
+ canvas.width = canvas.clientWidth;
753
+ canvas.height = canvas.clientHeight;
754
+ gl.viewport(0, 0, canvas.width, canvas.height);
755
+ });
756
+ </script>
757
+ </body>
758
+ </html>