LuciferDS commited on
Commit
461c732
·
verified ·
1 Parent(s): c6bf9e3

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +642 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Fractal Generator
3
- emoji: 😻
4
- colorFrom: purple
5
- colorTo: pink
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: fractal-generator
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: blue
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,642 @@
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>Fractal Universe Explorer</title>
7
+ <style>
8
+ @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&family=Ubuntu+Mono&display=swap');
9
+
10
+ :root {
11
+ --primary: #6c5ce7;
12
+ --secondary: #0984e3;
13
+ --dark: #2d3436;
14
+ --light: #dfe6e9;
15
+ --accent: #e17055;
16
+ }
17
+
18
+ * {
19
+ margin: 0;
20
+ padding: 0;
21
+ box-sizing: border-box;
22
+ }
23
+
24
+ body {
25
+ font-family: 'Montserrat', sans-serif;
26
+ background: linear-gradient(135deg, var(--dark), #000);
27
+ color: var(--light);
28
+ min-height: 100vh;
29
+ overflow-x: hidden;
30
+ }
31
+
32
+ .header {
33
+ text-align: center;
34
+ padding: 2rem;
35
+ position: relative;
36
+ }
37
+
38
+ .title {
39
+ font-size: 3.5rem;
40
+ margin-bottom: 1rem;
41
+ background: linear-gradient(to right, var(--primary), var(--secondary));
42
+ -webkit-background-clip: text;
43
+ background-clip: text;
44
+ color: transparent;
45
+ text-shadow: 0 0 10px rgba(108, 92, 231, 0.3);
46
+ }
47
+
48
+ .subtitle {
49
+ font-size: 1.2rem;
50
+ opacity: 0.8;
51
+ max-width: 800px;
52
+ margin: 0 auto;
53
+ line-height: 1.6;
54
+ }
55
+
56
+ .container {
57
+ display: flex;
58
+ flex-direction: column;
59
+ align-items: center;
60
+ padding: 2rem;
61
+ }
62
+
63
+ .controls {
64
+ display: flex;
65
+ flex-wrap: wrap;
66
+ gap: 1rem;
67
+ justify-content: center;
68
+ margin-bottom: 2rem;
69
+ width: 100%;
70
+ max-width: 900px;
71
+ }
72
+
73
+ .control-group {
74
+ display: flex;
75
+ flex-direction: column;
76
+ min-width: 150px;
77
+ }
78
+
79
+ label {
80
+ margin-bottom: 0.5rem;
81
+ font-size: 0.9rem;
82
+ opacity: 0.8;
83
+ }
84
+
85
+ select, input, button {
86
+ padding: 0.8rem 1rem;
87
+ border-radius: 8px;
88
+ border: 2px solid rgba(255, 255, 255, 0.1);
89
+ background: rgba(255, 255, 255, 0.05);
90
+ color: var(--light);
91
+ font-family: 'Ubuntu Mono', monospace;
92
+ transition: all 0.3s ease;
93
+ }
94
+
95
+ select:focus, input:focus, button:focus {
96
+ outline: none;
97
+ border-color: var(--primary);
98
+ box-shadow: 0 0 0 3px rgba(108, 92, 231, 0.3);
99
+ }
100
+
101
+ button {
102
+ cursor: pointer;
103
+ background: linear-gradient(135deg, var(--primary), var(--secondary));
104
+ color: white;
105
+ font-weight: bold;
106
+ text-transform: uppercase;
107
+ letter-spacing: 1px;
108
+ border: none;
109
+ }
110
+
111
+ button:hover {
112
+ transform: translateY(-2px);
113
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
114
+ }
115
+
116
+ .canvas-container {
117
+ position: relative;
118
+ width: 800px;
119
+ height: 600px;
120
+ margin: 0 auto;
121
+ border-radius: 16px;
122
+ overflow: hidden;
123
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
124
+ border: 2px solid rgba(255, 255, 255, 0.1);
125
+ }
126
+
127
+ canvas {
128
+ width: 100%;
129
+ height: 100%;
130
+ display: block;
131
+ }
132
+
133
+ .info-panel {
134
+ margin-top: 2rem;
135
+ max-width: 800px;
136
+ padding: 1.5rem;
137
+ background: rgba(0, 0, 0, 0.3);
138
+ border-radius: 16px;
139
+ line-height: 1.6;
140
+ }
141
+
142
+ .fractal-info {
143
+ margin-bottom: 1rem;
144
+ }
145
+
146
+ .fractal-title {
147
+ font-size: 1.5rem;
148
+ margin-bottom: 0.5rem;
149
+ color: var(--primary);
150
+ }
151
+
152
+ .fractal-description {
153
+ font-size: 0.95rem;
154
+ opacity: 0.9;
155
+ }
156
+
157
+ .loading {
158
+ position: absolute;
159
+ top: 0;
160
+ left: 0;
161
+ width: 100%;
162
+ height: 100%;
163
+ display: flex;
164
+ justify-content: center;
165
+ align-items: center;
166
+ background: rgba(0, 0, 0, 0.7);
167
+ z-index: 10;
168
+ opacity: 0;
169
+ pointer-events: none;
170
+ transition: opacity 0.3s ease;
171
+ }
172
+
173
+ .loading.active {
174
+ opacity: 1;
175
+ pointer-events: all;
176
+ }
177
+
178
+ .spinner {
179
+ width: 50px;
180
+ height: 50px;
181
+ border: 5px solid rgba(255, 255, 255, 0.1);
182
+ border-radius: 50%;
183
+ border-top-color: var(--primary);
184
+ animation: spin 1s ease-in-out infinite;
185
+ }
186
+
187
+ @keyframes spin {
188
+ to { transform: rotate(360deg); }
189
+ }
190
+
191
+ @media (max-width: 900px) {
192
+ .canvas-container {
193
+ width: 100%;
194
+ height: 500px;
195
+ }
196
+ }
197
+
198
+ @media (max-width: 600px) {
199
+ .title {
200
+ font-size: 2.5rem;
201
+ }
202
+
203
+ .subtitle {
204
+ font-size: 1rem;
205
+ }
206
+
207
+ .canvas-container {
208
+ height: 400px;
209
+ }
210
+ }
211
+ </style>
212
+ </head>
213
+ <body>
214
+ <div class="header">
215
+ <h1 class="title">Fractal Universe Explorer</h1>
216
+ <p class="subtitle">Explore the infinite complexity of mathematical fractals. Each fractal is generated in real-time using JavaScript, simulating what you might create with Python.</p>
217
+ </div>
218
+
219
+ <div class="container">
220
+ <div class="controls">
221
+ <div class="control-group">
222
+ <label for="fractal-type">Fractal Type</label>
223
+ <select id="fractal-type">
224
+ <option value="mandelbrot">Mandelbrot Set</option>
225
+ <option value="julia">Julia Set</option>
226
+ <option value="burning-ship">Burning Ship</option>
227
+ <option value="sierpinski">Sierpinski Triangle</option>
228
+ <option value="koch">Koch Snowflake</option>
229
+ </select>
230
+ </div>
231
+
232
+ <div class="control-group">
233
+ <label for="color-scheme">Color Scheme</label>
234
+ <select id="color-scheme">
235
+ <option value="rainbow">Rainbow</option>
236
+ <option value="fire">Fire</option>
237
+ <option value="ocean">Ocean</option>
238
+ <option value="monochrome">Monochrome</option>
239
+ <option value="pastel">Pastel</option>
240
+ </select>
241
+ </div>
242
+
243
+ <div class="control-group">
244
+ <label for="iterations">Iterations</label>
245
+ <input type="range" id="iterations" min="10" max="200" value="100">
246
+ <span id="iterations-value">100</span>
247
+ </div>
248
+
249
+ <div class="control-group">
250
+ <label for="zoom">Zoom Level</label>
251
+ <input type="range" id="zoom" min="1" max="20" value="1">
252
+ <span id="zoom-value">1x</span>
253
+ </div>
254
+
255
+ <button id="generate-btn">Generate Fractal</button>
256
+ </div>
257
+
258
+ <div class="canvas-container">
259
+ <canvas id="fractal-canvas"></canvas>
260
+ <div class="loading" id="loading">
261
+ <div class="spinner"></div>
262
+ </div>
263
+ </div>
264
+
265
+ <div class="info-panel">
266
+ <div class="fractal-info">
267
+ <h3 class="fractal-title">Mandelbrot Set</h3>
268
+ <p class="fractal-description" id="fractal-description">
269
+ The Mandelbrot set is the set of complex numbers c for which the function f(z) = z² + c does not diverge when iterated from z = 0.
270
+ It's one of the most famous fractals, exhibiting infinite complexity at every scale.
271
+ </p>
272
+ </div>
273
+ </div>
274
+ </div>
275
+
276
+ <script>
277
+ document.addEventListener('DOMContentLoaded', function() {
278
+ // DOM elements
279
+ const canvas = document.getElementById('fractal-canvas');
280
+ const ctx = canvas.getContext('2d');
281
+ const loading = document.getElementById('loading');
282
+ const generateBtn = document.getElementById('generate-btn');
283
+ const fractalType = document.getElementById('fractal-type');
284
+ const colorScheme = document.getElementById('color-scheme');
285
+ const iterations = document.getElementById('iterations');
286
+ const iterationsValue = document.getElementById('iterations-value');
287
+ const zoom = document.getElementById('zoom');
288
+ const zoomValue = document.getElementById('zoom-value');
289
+ const fractalDescription = document.getElementById('fractal-description');
290
+
291
+ // Canvas setup
292
+ function setupCanvas() {
293
+ const dpr = window.devicePixelRatio || 1;
294
+ const rect = canvas.getBoundingClientRect();
295
+ canvas.width = rect.width * dpr;
296
+ canvas.height = rect.height * dpr;
297
+ ctx.scale(dpr, dpr);
298
+ }
299
+
300
+ setupCanvas();
301
+ window.addEventListener('resize', setupCanvas);
302
+
303
+ // Update displayed values
304
+ iterations.addEventListener('input', function() {
305
+ iterationsValue.textContent = this.value;
306
+ });
307
+
308
+ zoom.addEventListener('input', function() {
309
+ zoomValue.textContent = `${this.value}x`;
310
+ });
311
+
312
+ // Fractal descriptions
313
+ const fractalDescriptions = {
314
+ 'mandelbrot': 'The Mandelbrot set is the set of complex numbers c for which the function f(z) = z² + c does not diverge when iterated from z = 0. It\'s one of the most famous fractals, exhibiting infinite complexity at every scale.',
315
+ 'julia': 'Julia sets are mathematically defined shapes that are closely related to the Mandelbrot set. Each Julia set corresponds to a different complex number c, determining the shape and complexity of the fractal pattern.',
316
+ 'burning-ship': 'The Burning Ship fractal is a variation of the Mandelbrot set that uses a slightly different iteration formula. It reveals ship-like structures that appear to be burning, hence the name.',
317
+ 'sierpinski': 'The Sierpinski triangle is a fractal that can be created by recursively subdividing an equilateral triangle into smaller equilateral triangles. It demonstrates perfect self-similarity at all scales.',
318
+ 'koch': 'The Koch snowflake is a fractal constructed by starting with an equilateral triangle, then recursively altering each line segment to add a triangular bump. It has an infinite perimeter but finite area.'
319
+ };
320
+
321
+ // Color palettes
322
+ const palettes = {
323
+ 'rainbow': (t) => {
324
+ const r = Math.floor((1 - t) * 255);
325
+ const g = Math.floor((Math.sin(t * Math.PI * 2) * 0.5 + 0.5) * 255);
326
+ const b = Math.floor(t * 255);
327
+ return `rgb(${r}, ${g}, ${b})`;
328
+ },
329
+ 'fire': (t) => {
330
+ const r = 255;
331
+ const g = Math.floor(t * 128 + 127);
332
+ const b = Math.floor(t * 50);
333
+ return `rgb(${r}, ${g}, ${b})`;
334
+ },
335
+ 'ocean': (t) => {
336
+ const r = 0;
337
+ const g = Math.floor(t * 100 + 50);
338
+ const b = Math.floor(t * 200 + 55);
339
+ return `rgb(${r}, ${g}, ${b})`;
340
+ },
341
+ 'monochrome': (t) => {
342
+ const c = Math.floor(t * 255);
343
+ return `rgb(${c}, ${c}, ${c})`;
344
+ },
345
+ 'pastel': (t) => {
346
+ const r = Math.floor((1 - t) * 200);
347
+ const g = Math.floor((t * 0.5 + 0.5) * 200);
348
+ const b = Math.floor((Math.sin(t * Math.PI) + 1) * 100);
349
+ return `rgb(${r}, ${g}, ${b})`;
350
+ }
351
+ };
352
+
353
+ // Generate fractal
354
+ function generateFractal() {
355
+ loading.classList.add('active');
356
+
357
+ // Get selected values
358
+ const type = fractalType.value;
359
+ const palette = palettes[colorScheme.value];
360
+ const maxIter = parseInt(iterations.value);
361
+ const zoomLevel = parseInt(zoom.value);
362
+
363
+ // Update description
364
+ document.querySelector('.fractal-title').textContent =
365
+ fractalType.options[fractalType.selectedIndex].text;
366
+ fractalDescription.textContent = fractalDescriptions[type];
367
+
368
+ // Clear and prepare canvas
369
+ const width = canvas.width;
370
+ const height = canvas.height;
371
+
372
+ // Use setTimeout to prevent UI freeze during heavy computation
373
+ setTimeout(() => {
374
+ // Draw selected fractal
375
+ switch(type) {
376
+ case 'mandelbrot':
377
+ drawMandelbrot(width, height, palette, maxIter, zoomLevel);
378
+ break;
379
+ case 'julia':
380
+ drawJulia(width, height, palette, maxIter, zoomLevel);
381
+ break;
382
+ case 'burning-ship':
383
+ drawBurningShip(width, height, palette, maxIter, zoomLevel);
384
+ break;
385
+ case 'sierpinski':
386
+ drawSierpinski(width, height, palette, maxIter);
387
+ break;
388
+ case 'koch':
389
+ drawKochSnowflake(width, height, palette, Math.floor(maxIter/15));
390
+ break;
391
+ }
392
+
393
+ loading.classList.remove('active');
394
+ }, 100);
395
+ }
396
+
397
+ // Mandelbrot set
398
+ function drawMandelbrot(width, height, palette, maxIter, zoomLevel) {
399
+ const zoomFactor = Math.pow(2, zoomLevel);
400
+ const centerX = -0.5;
401
+ const centerY = 0;
402
+ const scale = 2 / (Math.min(width, height) / 2) / zoomFactor;
403
+
404
+ const imageData = ctx.createImageData(width, height);
405
+ const data = imageData.data;
406
+
407
+ for (let px = 0; px < width; px++) {
408
+ for (let py = 0; py < height; py++) {
409
+ // Convert pixel coordinates to complex plane
410
+ const x0 = (px - width/2) * scale + centerX;
411
+ const y0 = (py - height/2) * scale + centerY;
412
+
413
+ let x = 0;
414
+ let y = 0;
415
+ let iter = 0;
416
+
417
+ // Iterate until escape or max iterations
418
+ while (x*x + y*y <= 4 && iter < maxIter) {
419
+ const x_new = x*x - y*y + x0;
420
+ y = 2*x*y + y0;
421
+ x = x_new;
422
+ iter++;
423
+ }
424
+
425
+ // Color based on iterations
426
+ const color = palette(iter/maxIter);
427
+ const [r, g, b] = color.match(/\d+/g);
428
+ const idx = (px + py * width) * 4;
429
+
430
+ // Set pixel color
431
+ if (iter === maxIter) {
432
+ // Inside the set (black)
433
+ data[idx] = 0;
434
+ data[idx+1] = 0;
435
+ data[idx+2] = 0;
436
+ } else {
437
+ // Outside (colored by iterations)
438
+ data[idx] = r;
439
+ data[idx+1] = g;
440
+ data[idx+2] = b;
441
+ }
442
+ data[idx+3] = 255; // Alpha
443
+ }
444
+ }
445
+
446
+ ctx.putImageData(imageData, 0, 0);
447
+ }
448
+
449
+ // Julia set (similar to Mandelbrot but with fixed c)
450
+ function drawJulia(width, height, palette, maxIter, zoomLevel) {
451
+ const zoomFactor = Math.pow(2, zoomLevel);
452
+ const centerX = 0;
453
+ const centerY = 0;
454
+ const scale = 2.5 / (Math.min(width, height) / 2) / zoomFactor;
455
+
456
+ // Julia parameters (can be randomized)
457
+ const cRe = -0.7;
458
+ const cIm = 0.27;
459
+
460
+ const imageData = ctx.createImageData(width, height);
461
+ const data = imageData.data;
462
+
463
+ for (let px = 0; px < width; px++) {
464
+ for (let py = 0; py < height; py++) {
465
+ const zx = (px - width/2) * scale + centerX;
466
+ const zy = (py - height/2) * scale + centerY;
467
+
468
+ let x = zx;
469
+ let y = zy;
470
+ let iter = 0;
471
+
472
+ while (x*x + y*y <= 4 && iter < maxIter) {
473
+ const x_new = x*x - y*y + cRe;
474
+ y = 2*x*y + cIm;
475
+ x = x_new;
476
+ iter++;
477
+ }
478
+
479
+ const color = palette(iter/maxIter);
480
+ const [r, g, b] = color.match(/\d+/g);
481
+ const idx = (px + py * width) * 4;
482
+
483
+ data[idx] = (iter === maxIter) ? 0 : r;
484
+ data[idx+1] = (iter === maxIter) ? 0 : g;
485
+ data[idx+2] = (iter === maxIter) ? 0 : b;
486
+ data[idx+3] = 255;
487
+ }
488
+ }
489
+
490
+ ctx.putImageData(imageData, 0, 0);
491
+ }
492
+
493
+ // Burning Ship fractal
494
+ function drawBurningShip(width, height, palette, maxIter, zoomLevel) {
495
+ const zoomFactor = Math.pow(2, zoomLevel);
496
+ const centerX = -0.5;
497
+ const centerY = -0.5;
498
+ const scale = 2.8 / (Math.min(width, height) / 2) / zoomFactor;
499
+
500
+ const imageData = ctx.createImageData(width, height);
501
+ const data = imageData.data;
502
+
503
+ for (let px = 0; px < width; px++) {
504
+ for (let py = 0; py < height; py++) {
505
+ const x0 = (px - width/2) * scale + centerX;
506
+ const y0 = (py - height/2) * scale + centerY;
507
+
508
+ let x = 0;
509
+ let y = 0;
510
+ let iter = 0;
511
+
512
+ while (x*x + y*y <= 4 && iter < maxIter) {
513
+ const x_new = x*x - y*y + x0;
514
+ y = Math.abs(2*x*y) + y0;
515
+ x = Math.abs(x_new);
516
+ iter++;
517
+ }
518
+
519
+ const color = palette(iter/maxIter);
520
+ const [r, g, b] = color.match(/\d+/g);
521
+ const idx = (px + py * width) * 4;
522
+
523
+ data[idx] = (iter === maxIter) ? 0 : r;
524
+ data[idx+1] = (iter === maxIter) ? 0 : g;
525
+ data[idx+2] = (iter === maxIter) ? 0 : b;
526
+ data[idx+3] = 255;
527
+ }
528
+ }
529
+
530
+ ctx.putImageData(imageData, 0, 0);
531
+ }
532
+
533
+ // Sierpinski Triangle (geometric fractal)
534
+ function drawSierpinski(width, height, palette, maxIter) {
535
+ ctx.fillStyle = 'black';
536
+ ctx.fillRect(0, 0, width, height);
537
+
538
+ const size = Math.min(width, height) * 0.9;
539
+ const marginX = (width - size) / 2;
540
+ const marginY = (height - size) / 2;
541
+
542
+ // Starting points (triangle)
543
+ let points = [
544
+ {x: marginX + size/2, y: marginY},
545
+ {x: marginX, y: marginY + size},
546
+ {x: marginX + size, y: marginY + size}
547
+ ];
548
+
549
+ // Random starting point
550
+ let px = Math.random() * width;
551
+ let py = Math.random() * height;
552
+
553
+ // Draw many iterations
554
+ for (let i = 0; i < maxIter * 300; i++) {
555
+ // Choose a random corner
556
+ const corner = Math.floor(Math.random() * 3);
557
+
558
+ // Move halfway toward that corner
559
+ px = (px + points[corner].x) / 2;
560
+ py = (py + points[corner].y) / 2;
561
+
562
+ // Draw point
563
+ ctx.fillStyle = palette(i/(maxIter*300));
564
+ ctx.fillRect(px, py, 1, 1);
565
+ }
566
+ }
567
+
568
+ // Koch Snowflake (recursive fractal)
569
+ function drawKochSnowflake(width, height, palette, depth) {
570
+ ctx.fillStyle = 'black';
571
+ ctx.fillRect(0, 0, width, height);
572
+
573
+ const size = Math.min(width, height) * 0.7;
574
+ const centerX = width / 2;
575
+ const centerY = height / 2;
576
+
577
+ // Starting equilateral triangle
578
+ let points = [
579
+ {x: centerX, y: centerY - size/2},
580
+ {x: centerX - size/2, y: centerY + size/2},
581
+ {x: centerX + size/2, y: centerY + size/2}
582
+ ];
583
+
584
+ // Draw the snowflake
585
+ ctx.strokeStyle = palette(1.0);
586
+ ctx.lineWidth = 1;
587
+
588
+ function koch(p1, p2, level) {
589
+ if (level === 0) {
590
+ ctx.beginPath();
591
+ ctx.moveTo(p1.x, p1.y);
592
+ ctx.lineTo(p2.x, p2.y);
593
+ ctx.strokeStyle = palette(1 - level/depth);
594
+ ctx.stroke();
595
+ } else {
596
+ const dx = p2.x - p1.x;
597
+ const dy = p2.y - p1.y;
598
+
599
+ // Calculate intermediate points
600
+ const a = {
601
+ x: p1.x + dx / 3,
602
+ y: p1.y + dy / 3
603
+ };
604
+
605
+ const b = {
606
+ x: p1.x + dx / 2 - dy * Math.sqrt(3)/6,
607
+ y: p1.y + dy / 2 + dx * Math.sqrt(3)/6
608
+ };
609
+
610
+ const c = {
611
+ x: p1.x + 2*dx/3,
612
+ y: p1.y + 2*dy/3
613
+ };
614
+
615
+ // Recursively draw the 4 segments
616
+ koch(p1, a, level - 1);
617
+ koch(a, b, level - 1);
618
+ koch(b, c, level - 1);
619
+ koch(c, p2, level - 1);
620
+ }
621
+ }
622
+
623
+ // Draw the three initial sides
624
+ koch(points[0], points[1], depth);
625
+ koch(points[1], points[2], depth);
626
+ koch(points[2], points[0], depth);
627
+ }
628
+
629
+ // Events
630
+ generateBtn.addEventListener('click', generateFractal);
631
+ fractalType.addEventListener('change', function() {
632
+ document.querySelector('.fractal-title').textContent =
633
+ this.options[this.selectedIndex].text;
634
+ fractalDescription.textContent = fractalDescriptions[this.value];
635
+ });
636
+
637
+ // Generate initial fractal
638
+ generateFractal();
639
+ });
640
+ </script>
641
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
642
+ </html>