-
Notifications
You must be signed in to change notification settings - Fork 26
/
Copy pathindex.html
702 lines (667 loc) · 60.8 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="My Midjourney AI Art">
<title>My Midjourney AI Art</title>
<meta property="og:image" content="https://cdn.midjourney.com/28df4066-dc0c-4741-a3da-3120c0b547c1/grid_0_384_N.webp"/>
<meta property="og:description" content="Gallery of art generated through Midjourney AI" />
<style>
html {
/* inline background noise. No network request & failure & white flash at page load */
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAAUVBMVEWFhYWDg4N3d3dtbW17e3t1dXWBgYGHh4d5eXlzc3OLi4ubm5uVlZWPj4+NjY19fX2JiYl/f39ra2uRkZGZmZlpaWmXl5dvb29xcXGTk5NnZ2c8TV1mAAAAG3RSTlNAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAvEOwtAAAFVklEQVR4XpWWB67c2BUFb3g557T/hRo9/WUMZHlgr4Bg8Z4qQgQJlHI4A8SzFVrapvmTF9O7dmYRFZ60YiBhJRCgh1FYhiLAmdvX0CzTOpNE77ME0Zty/nWWzchDtiqrmQDeuv3powQ5ta2eN0FY0InkqDD73lT9c9lEzwUNqgFHs9VQce3TVClFCQrSTfOiYkVJQBmpbq2L6iZavPnAPcoU0dSw0SUTqz/GtrGuXfbyyBniKykOWQWGqwwMA7QiYAxi+IlPdqo+hYHnUt5ZPfnsHJyNiDtnpJyayNBkF6cWoYGAMY92U2hXHF/C1M8uP/ZtYdiuj26UdAdQQSXQErwSOMzt/XWRWAz5GuSBIkwG1H3FabJ2OsUOUhGC6tK4EMtJO0ttC6IBD3kM0ve0tJwMdSfjZo+EEISaeTr9P3wYrGjXqyC1krcKdhMpxEnt5JetoulscpyzhXN5FRpuPHvbeQaKxFAEB6EN+cYN6xD7RYGpXpNndMmZgM5Dcs3YSNFDHUo2LGfZuukSWyUYirJAdYbF3MfqEKmjM+I2EfhA94iG3L7uKrR+GdWD73ydlIB+6hgref1QTlmgmbM3/LeX5GI1Ux1RWpgxpLuZ2+I+IjzZ8wqE4nilvQdkUdfhzI5QDWy+kw5Wgg2pGpeEVeCCA7b85BO3F9DzxB3cdqvBzWcmzbyMiqhzuYqtHRVG2y4x+KOlnyqla8AoWWpuBoYRxzXrfKuILl6SfiWCbjxoZJUaCBj1CjH7GIaDbc9kqBY3W/Rgjda1iqQcOJu2WW+76pZC9QG7M00dffe9hNnseupFL53r8F7YHSwJWUKP2q+k7RdsxyOB11n0xtOvnW4irMMFNV4H0uqwS5ExsmP9AxbDTc9JwgneAT5vTiUSm1E7BSflSt3bfa1tv8Di3R8n3Af7MNWzs49hmauE2wP+ttrq+AsWpFG2awvsuOqbipWHgtuvuaAE+A1Z/7gC9hesnr+7wqCwG8c5yAg3AL1fm8T9AZtp/bbJGwl1pNrE7RuOX7PeMRUERVaPpEs+yqeoSmuOlokqw49pgomjLeh7icHNlG19yjs6XXOMedYm5xH2YxpV2tc0Ro2jJfxC50ApuxGob7lMsxfTbeUv07TyYxpeLucEH1gNd4IKH2LAg5TdVhlCafZvpskfncCfx8pOhJzd76bJWeYFnFciwcYfubRc12Ip/ppIhA1/mSZ/RxjFDrJC5xifFjJpY2Xl5zXdguFqYyTR1zSp1Y9p+tktDYYSNflcxI0iyO4TPBdlRcpeqjK/piF5bklq77VSEaA+z8qmJTFzIWiitbnzR794USKBUaT0NTEsVjZqLaFVqJoPN9ODG70IPbfBHKK+/q/AWR0tJzYHRULOa4MP+W/HfGadZUbfw177G7j/OGbIs8TahLyynl4X4RinF793Oz+BU0saXtUHrVBFT/DnA3ctNPoGbs4hRIjTok8i+algT1lTHi4SxFvONKNrgQFAq2/gFnWMXgwffgYMJpiKYkmW3tTg3ZQ9Jq+f8XN+A5eeUKHWvJWJ2sgJ1Sop+wwhqFVijqWaJhwtD8MNlSBeWNNWTa5Z5kPZw5+LbVT99wqTdx29lMUH4OIG/D86ruKEauBjvH5xy6um/Sfj7ei6UUVk4AIl3MyD4MSSTOFgSwsH/QJWaQ5as7ZcmgBZkzjjU1UrQ74ci1gWBCSGHtuV1H2mhSnO3Wp/3fEV5a+4wz//6qy8JxjZsmxxy5+4w9CDNJY09T072iKG0EnOS0arEYgXqYnXcYHwjTtUNAcMelOd4xpkoqiTYICWFq0JSiPfPDQdnt+4/wuqcXY47QILbgAAAABJRU5ErkJggg==);
background-color: rgb(1 5 19);
}
body {
overflow-x: hidden; /* 1D mode places images horizontally and exceeds viewport width */
margin: 0;
}
#github-icon {
position: absolute;
top: 12px;
right: 12px;
transition: transform 0.2s ease-out;
}
#github-icon:hover {
transform: scale(1.1);
}
.box {
position: absolute;
overflow: hidden;
background-color: rgb(34, 36, 53);
border-radius: 16px;
box-shadow:
0 0px 2px 0 rgba(255, 255, 255, 0.2), /* shadow acting as border. Won't affect layout inside or outside */
0 4px 28px 0 rgba(0, 0, 0, 0.4); /* actual shadow */
background-size: 100%; /* low-res image added as background-image in JS later. Saves 1 DOM node */
background-repeat: no-repeat; /* just in case of floating point whatever */
}
.box img {
display: block; /* prevent inline-block from adding whitespaces... */
width: 100%;
}
.prompt {
position: absolute; /* rest near bottom even when image isn't loaded yet */
width: 100%;
box-sizing: border-box;
padding: 8px 12px 0px 12px;
font-family: "DM Sans", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* same as midjourney's */
color: rgb(192, 198, 205);
font-size: 16px;
cursor: text; /* overrides document's cursor when needed (see JS code below) */
/* for programmatic multiline clamping */
display: -webkit-box;
-webkit-box-orient: vertical;
}
.prompt:before {
content: '❝';
color: rgb(122, 126, 130);
padding-right: 2px;
}
</style>
</head>
<body>
<a href="https://github.com/chenglou/chenglou.github.io" target="_blank" id="github-icon">
<svg width="32" viewbox="0 0 98 96"> <!-- from https://github.com/logos, added viewbox, removed original width & height (aka resized) -->
<path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#fff"/>
</svg>
</a>
<script>
'use strict'
// === generic scheduler & its debugger
let scheduledRender = false
function scheduleRender() {
if (scheduledRender) return;
scheduledRender = true
requestAnimationFrame(function renderAndMaybeScheduleAnotherRender(now) { // eye-grabbing name. No "(anonymous)" function in the debugger & profiler
scheduledRender = false
if (render(now)) scheduleRender()
})
}
// === generic spring physics
// 4ms/step for the spring animation's step. Typically 4 steps for 60fps (16.6ms/frame) and 2 for 120fps (8.3ms/frame). Frame time delta varies, so not always true
// could use 8ms instead, but 120fps' 8.3ms/frame means the computation might not fit in the remaining 0.3ms, which means sometime the simulation step wouldn't even run once, giving the illusion of jank
const msPerAnimationStep = 4
function spring(pos, v = 0, k = 290, b = 30) {
return {pos, dest: pos, v, k, b} // k = stiffness, b = damping. Try https://chenglou.me/react-motion/demos/demo5-spring-parameters-chooser/
}
function springStep(config) {
// https://blog.maximeheckel.com/posts/the-physics-behind-spring-animations/
// this seems inspired by https://github.com/chenglou/react-motion/blob/9e3ce95bacaa9a1b259f969870a21c727232cc68/src/stepper.js
const t = msPerAnimationStep / 1000 // convert to seconds for the physics equation
const {pos, dest, v, k, b} = config
// for animations, dest is actually spring at rest. Current position is the spring's stretched/compressed state
const Fspring = -k * (pos - dest) // Spring stiffness, in kg / s^2
const Fdamper = -b * v // Damping, in kg / s
const a = Fspring + Fdamper // a needs to be divided by mass, but we'll assume mass of 1. Adjust k and b to change spring curve instead
const newV = v + a * t
const newPos = pos + newV * t
config.pos = newPos; config.v = newV
}
function springGoToEnd(config) {
config.pos = config.dest
config.v = 0
}
// === generic helpers
function clamp(min, v, max) {
return v > max ? max : v < min ? min : v
}
// === constant layout metrics. The rest is dynamic
const promptPaddingBottom = 8
const promptSizeY = 47 + promptPaddingBottom // 47 is a magic number. Doesn't show the 3rd line nor cuts the 2nd line on safari, mobile safari and chrome
const prompt1DSizeY = 64 + promptPaddingBottom // size in 1d mode
const boxesGapX = 24, boxesGapY = 24
const boxes1DGapX = 52, boxes1DGapY = 28
const windowPaddingTop = 40
const gapTopPeek = 40 // used when programmatically scrolling and wanting to show some gap at the top of a row
const hitArea1DSizeX = 100 // left and right click region in 1D mode
function colsBoxMaxSizeXF(containerSizeX) {
const boxMinSizeX = 220 // Make sure that on mobile, this min width is big enough not to show 2 images per row. Also, this won't be respected if view's tiny
const cols = clamp(1, Math.floor((containerSizeX - boxesGapX) / (boxMinSizeX + boxesGapX)), 7) // half of boxesGapX for container's left and right gap
const boxMaxSizeX = (containerSizeX - boxesGapX - cols * boxesGapX) / cols
return {cols, boxMaxSizeX}
}
const isSafari = navigator.userAgent.includes('Safari') && !navigator.userAgent.includes('Chrome') // Chrome also includes Safari in user-agent string
if (isSafari) {
// alright *deep breath* Desktop Safari is fine, but iPad Safari behaves badly when items exceed the viewport in 1D mode (overflow hidden doesn't work!). This is prominent if you hold arrow right and check the GitHub logo move. It's especially pathological on Stage Manager and any other iPad Safari mode where the app window shrinks and bugs the browser more for whatever reason
// so we use contain: layout plus viewport width and height to force the clipping of items. Now every browser behaves well with these, BUT Chrome doesn't have rubberbanding of inner elements (only page-wide one). So YES I'm switching to scrolling page instead of document body for Chrome JUST FOR THE RUBBER BANDING on macOS.
// this is how much I care about edge scroll rubber banding. Thanks Bas Ording & old Apple. If browser specs folks were more visual & interactions-driven as opposed to being static document-driven then we wouldn't have a decade-long decline of visual & interaction design as a discipline.
// it's a good thing these hacks are easy to pull off under this architecture, and quite readable and even robust despite changing whole container logics
document.body.style.contain = 'layout'
document.body.style.width = '100vw'
document.body.style.height = '100vh'
}
// === state. Plus one in the URL's hash
const debug = false // toggle this for manually stepping through animation frames (press key A)
let debugTimestamp = 0
let animatedUntilTime = null
let reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)')
let anchor = 0 // keep a box stable during resize layout shifts
let windowSizeX = document.documentElement.clientWidth
let scrollY = isSafari ? document.body.scrollTop : window.scrollY
let pointer = {x: -Infinity, y: -Infinity} // btw, on page load, there's no way to render a first cursor state =(
let events = { keydown: null, click: null, mousemove: null }
let data; {
const windowSizeY = document.documentElement.clientHeight
const {cols, boxMaxSizeX} = colsBoxMaxSizeXF(windowSizeX)
const imgMaxSizeY = boxMaxSizeX + 100 // TODO: adjust this better
data = [
{id: 'd228330c-7364-497b-994f-a37467ae436a/0_0', w: 2048, h: 2048 , prompt: `Miniature watermelon delicately balanced on fingertip`},
{id: '7b5301ca-ef01-44bd-8bcc-4473c945aaaf/0_0', w: 1792, h: 2688 , prompt: `Hourglass heart, liquid, grand canyon background, fleeting, ephemerality of time`},
{id: '96ff81dd-fa46-43c0-b681-35b7cd8eb700/0_0', w: 1792, h: 2688 , prompt: `A phone held extremely close against an xray of a skeleton sitting on a sofa, side by side view`},
{id: 'd9fb18cf-a37e-4b1a-b10e-678855722fa3/0_0', w: 1632, h: 2912, prompt: `Japanese girl, very detailed, close up, sharp skin, kodak camera`},
{id: '077ddfbf-4f1a-437f-909e-073ec48beae0/0_0', w: 1344, h: 896, prompt: `Eclipse in a tiny keychain bottle, dangling around a walking woman's purse, dynamic pose, macro photography`},
{id: 'e1d06d80-68ca-4081-9ca2-73f5ba7333cc/0_3', w: 1536, h: 768, prompt: `Tetris monuments in lord of the rings setting`},
{id: '32e02d27-a98a-4ef0-a159-368edec4e632/0_1', w: 1344, h: 896, prompt: `Octopus-like desk legs in outer space`},
{id: 'baab508f-4a59-45af-a784-9c47db3cda17/0_3', w: 1344, h: 896, prompt: `Jupiter planet sitting on a chair on Saturn`},
{id: 'cc8493df-ca6b-4205-9bb1-af00d3dc736e/0_0', w: 1344, h: 896, prompt: `The Garden of Earthly Delights, symmetric spiral painting`},
{id: '196ff609-69c8-47be-8d92-e9ba9db95e2c/0_3', w: 848, h: 1424, prompt: `Peaceful prehistoric marine lagaxy life, adventure, bold outlines, bold colors, psychedelic swirls patterns`},
{id: 'daa6883b-75ae-4805-9fe8-d394006ca8ca/0_1', w: 1024, h: 1024, prompt: `Apple WWDC promo, dan mumford`},
{id: '0dcaf55a-6ece-42b1-a0b3-eb753d108415/0_3', w: 1424, h: 848, prompt: `Everything everywhere all at once, bold outlines`},
{id: '037086ce-0dec-4888-8938-13ef27b49c9d/0_0', w: 1344, h: 896, prompt: `Kid's back, low angle, staring up at mobius spaceship landing in ancient china`},
{id: 'cfd235ab-89b2-4834-9544-19768f160d2a/0_1', w: 1344, h: 896, prompt: `Cute and fluffy marshmallow modern mansion`},
{id: 'ab740a8e-54af-452a-9b74-7b8cb233c025/grid_0', w: 1024, h: 1024, prompt: `Voronoi 3D printed cup taking roots`},
{id: 'bcf32fdc-6d81-40f0-9063-d67cb402ea2a/grid_0', w: 1344, h: 1024, prompt: `Marble sculpture of an orange, museum gallery, exhibit`},
{id: 'eb145f80-0196-4557-8560-e33637a3a807/grid_0', w: 846, h: 1728, prompt: `hourglass heart, liquid, grand canyon background`},
{id: 'e7096121-02c0-4b46-b74e-c8d50f1cf82f/grid_0', w: 846, h: 1728, prompt: `hourglass heart, liquid, dynamic background`},
{id: '604af437-f76b-4c7f-b778-1a3bf6ad663b/grid_0', w: 1024, h: 1024, prompt: `Golden retriever rising from the thick smokes, silhouette, contour`},
{id: 'b6beb80d-b006-45e0-a06d-5e482b541571/grid_0', w: 2304, h: 1536, prompt: `Jupiter sitting on a chair on Saturn`},
{id: 'c745ac87-7a5a-4f6e-a44e-c492e5ba9a1f/grid_0', w: 1024, h: 1024, prompt: `beautiful hyperrealistic abstract neural network colony, morning dew, depth of field`},
{id: 'a70b5a2e-046c-4e7c-a9d1-d1d9de5833dd/grid_0', w: 1024, h: 1024, prompt: `cute Christmas knolling`},
{id: '30d33a33-9b5b-4947-b813-4d0c5c2f9939/grid_0', w: 1536, h: 1024, prompt: `Chinese mid autumn festival on Neptune, surrealism`},
{id: 'fb622e95-814d-4f74-8ac0-04d266c8387d/grid_0', w: 1664, h: 2432, prompt: `eclipse in a bottle`},
{id: '91a44825-dc86-4166-bd9a-4b547bcad830/grid_0', w: 1664, h: 2432, prompt: `eclipse in a bottle`},
{id: '418aae43-2209-4d98-a2a9-b25d3b447e2c/grid_0', w: 1536, h: 1024, prompt: `city, interconnected cable wires toward giant server tower in the center, dan mumford, bright morning light`},
{id: '8ea0f4da-a9fc-4880-8f71-f51bf6c7f3d1/grid_0', w: 1536, h: 1024, prompt: `city, interconnected cable wires toward giant server tower in the center, dan mumford, bright morning light`},
{id: 'b4652c7e-44ee-4d3b-b385-9c5c237f177b/grid_0', w: 1536, h: 1024, prompt: `werewolf princess warrior, tower, Berserk manga style, black and white, by Kentaro Miura`},
{id: '733a5b94-3f35-4357-8ef5-645d1a9bb942/grid_0', w: 1024, h: 1536, prompt: `silk-like silhouette of koi fish, abstract wall paper, volumetric lighting`},
{id: '1c195159-f1b0-4f23-a605-dd30ad42bbe5/grid_0', w: 1536, h: 1024, prompt: `giant fearsome beast god descending upon the sky, james jean`},
{id: '0a1d96be-c49e-4769-a45f-3581500f1e19/grid_0', w: 1024, h: 1024, prompt: `dancing female astronaut exploring a floating food planet, bold colors, bold outlines, tshirt design`},
{id: '01c5a300-1597-4f45-99f8-c9cafe21925c/grid_0', w: 1536, h: 1024, prompt: `damascus steel metal zebra in a museum exhibit, dynamic running pose, bright morning daylight, low angle`},
{id: 'b88c0228-6442-4892-83ad-99cac2059abc/grid_0', w: 1024, h: 1536, prompt: `incredibly overloaded salmon crab shrimp octopus beef tomato lettuce onion noodle tornado floating tilted burger, promo photoshoot, spinning motion, explosive sea waves background, bright daylight`},
{id: '2ca7a0f7-9153-45ce-be2b-1fc89327f61b/grid_0', w: 2304, h: 1536, prompt: `futuristic qipao, pink seamless astronaut skirt, sitting cross-legged at a floating space cafe, looking at camera, high angle, depth of field`},
{id: 'ec03f17f-d94e-46cb-999b-9c1678943ce1/grid_0', w: 2304, h: 1536, prompt: `futuristic qipao, pink seamless astronaut skirt, sitting cross-legged at a floating space cafe, looking at camera, high angle, depth of field`},
{id: '45737e8e-d560-41d8-892f-fa498b9b5fe6/grid_0', w: 1536, h: 1024, prompt: `futuristic nurse, tight seamless astronaut qipao, slender skirt thighs, sitting cross-legged at a floating space cafe, dynamic angle, depth of field`},
{id: '76300f77-5b1a-4b5c-96a3-03a26483027c/grid_0', w: 1536, h: 1024, prompt: `concentric space station city, white glowing global illumination, earth space background, Christopher Nolan style, 70mm film shot`},
{id: '890a8dbb-93f6-4934-a92f-7dde390410e0/grid_0', w: 1024, h: 1536, prompt: `website landing page of a company with photos of streets and tiles`},
{id: '11048abd-2c7e-42de-929b-bad5d810b3a3/grid_0', w: 1024, h: 1024, prompt: `dead tree and castle shaped like the silhouette of a skull, negative space`},
{id: 'fc649e18-8176-4673-93cc-cdfa9b9573bb/grid_0', w: 1024, h: 1536, prompt: `giant skeletal guanyin descending over small red offerings, cinematic atmosphere`},
{id: '8dbdb64c-c779-41be-b4da-4d658bea0520/grid_0', w: 1536, h: 1024, prompt: `giant skeletal guanyin descending over small red offerings, cinematic atmosphere`},
{id: 'da3fafd3-2d41-4a98-a6c4-06a607445615/grid_0', w: 1536, h: 1024, prompt: `cyberpunk haruhi suzumiya, cowboy bebop anime style, city background`},
{id: '903a0743-9c23-4900-b163-923d0b20d330/grid_0', w: 1024, h: 1024, prompt: `dancing female astronaut exploring a floating food planet, bold colors, bold outlines, tshirt design`},
{id: '0db9894f-4569-494c-8c0b-1e1dcfdcf347/grid_0', w: 1024, h: 1024, prompt: `dancing female astronaut exploring a floating food planet, bold colors, bold outlines, tshirt design`},
{id: '3cebad25-2dc2-4d6d-85a1-7ee1604cd536/grid_0', w: 1024, h: 1536, prompt: `asian girl in scarf and glasses with haku dragon from spirited away`},
{id: 'a812ad46-f902-427e-804b-68f5a55f2264/grid_0', w: 1024, h: 1024, prompt: `closed up macrophotography of a bowl of asian dessert`},
{id: 'ad1ee2cf-b153-4e8c-a4cc-84a1eb087f2a/grid_0', w: 2048, h: 2048, prompt: `air fryer the shape of Totoro's mouth, marketing photo`},
{id: 'dc6e61f7-6eef-40aa-951f-d96c7878ef4a/grid_0', w: 1024, h: 1024, prompt: `metal slug boss level, pixel art`},
{id: 'cd6b3484-ad5a-49df-995e-93524cd8d5d2/grid_0', w: 1024, h: 1024, prompt: `laundry hanging on the line, forming the contour of a cow, surreal photo`},
{id: '1ae27dea-ea5a-44f7-8917-4363d62dc581/grid_0', w: 1024, h: 1024, prompt: `beautiful delicious pie chart`},
{id: '4481ad84-444d-4761-adb1-15d470f216da/grid_0', w: 1536, h: 1536, prompt: `the cutest little cartoon octopus wearing a cap in a bathtub, she's playing with a toy boat, studio lighting, closed-up, hyperrealistic, photoshoot, tilt shift, contrast, intricate details`},
{id: 'b76d9447-55a7-4a5d-8010-c87a51c9b8de/grid_0', w: 1024, h: 1024, prompt: `cute little acorn doll with glasses in the forest, tilt shift, intricate details, 8k, photoshoot, contrast, studio lighting`},
{id: '567f1757-d3e4-4bf6-9bd5-493179ebf88d/grid_0', w: 1024, h: 1024, prompt: `wolf god descending from sky, kim jung gi`},
{id: 'ac21e3cc-fa37-4ef6-89fe-c0539a956b85/grid_0', w: 1024, h: 1024, prompt: `dolphin submarine pod flying in space, saturn background, bold outlines`},
{id: '87779bb2-e5fd-44f0-99a8-b354cbb63536/grid_0', w: 1024, h: 1024, prompt: `beggar joker`},
{id: '16939df8-d6d0-4027-9175-aaa2102ac563/grid_0', w: 1024, h: 1024, prompt: `space elevator tree, bold outlines`},
{id: '239b1670-a242-437a-b787-c5bff453ab49/grid_0', w: 1024, h: 1024, prompt: `san francisco intersection, fisheye lens, art nouveau cartoon style`},
{id: '28df4066-dc0c-4741-a3da-3120c0b547c1/grid_0', w: 1024, h: 1024, prompt: `astronaut exploring a floating food planet`},
{id: '50be9117-b2f5-482f-b61f-46477ed6184e/grid_0', w: 1024, h: 1536, prompt: `three-body problem, cinematic`},
{id: '39428453-9b30-4998-a3d6-4668b4e2621f/grid_0', w: 1536, h: 1536, prompt: `super mario gundam`},
{id: '9e714cfb-beec-4255-9ef2-672b9a6c584b/grid_0', w: 1024, h: 1024, prompt: `zebra strips manga portraits`},
{id: '3178e18f-43b7-4732-addc-33a7325ee508/grid_0', w: 1024, h: 1024, prompt: `Thalassophobia`},
{id: '04eee122-3df7-4c20-ad75-de391d3558f9/grid_0', w: 1024, h: 1024, prompt: `thanos as a Snapple juice bottle, spilled sideway onto the table`},
{id: 'eb8b5142-e242-4435-bd17-a2b1a9e71d6d/grid_0', w: 1024, h: 1024, prompt: `steve jobs jedi with a red lightsaber`},
{id: '30478c95-4d9a-41c0-b74c-86ea6296a133/grid_0', w: 1024, h: 1024, prompt: `Everything everywhere all at once`},
{id: '2146701f-fcb9-4709-a0e5-307b2682036c/grid_0', w: 1024, h: 1024, prompt: `delicious cotton candies keyboard`},
{id: 'f35c9b1f-696d-4837-93c1-1197db22a2a1/grid_0', w: 1024, h: 1024, prompt: `giant skeletal guanyin descending over small red offerings, cinematic atmosphere`},
{id: '57d9ede4-ad7a-4b61-ba25-98946893c035/grid_0', w: 1024, h: 1024, prompt: `the letters "R e S c r i p t" clearly written with ink, perfect kerning`},
{id: '53c419f0-cf44-4413-9b8c-02ba4aa078a9/grid_0', w: 1664, h: 2432, prompt: `underground cave, giant hand-like ribcage bones tower, berserk`},
{id: 'adae74c4-65c5-4245-87e1-c0fbc81c2c27/grid_0', w: 1024, h: 1792, prompt: `liquid gold melting over white bird skull mask, cyberpunk rococo`},
{id: 'f0485122-e83e-492c-aa63-42a28015e45d/grid_0', w: 1664, h: 2432, prompt: `futuristic soldier with bird skull mask, rococo`},
{id: '8a8f7373-d0b2-4ea4-a3ff-a93e312d1aaf/grid_0', w: 1664, h: 2432, prompt: `skull knight, rococo`},
{id: '9589827f-4904-4dd8-a5a4-4fcc7a9c93f1/grid_0', w: 2304, h: 1536, prompt: `grim rococo skulls`},
{id: '6663e462-eba5-4a62-b914-5dc62d8be269/grid_0', w: 2048, h: 2048, prompt: `tiny comfy vertical house in the valley of sunshine and flowers`},
{id: '20fb2780-14bc-44af-8fee-bce4da244f9e/grid_0', w: 2304, h: 1536, prompt: `rainy fall, happy tree frog on a leaf, close up high-resolution macrophotography`},
{id: 'acc8432d-fc61-4346-8aee-961d4a51aa6d/grid_0', w: 2048, h: 2048, prompt: `delicious chocolate cake keyboard`},
{id: '3beb7a37-30a0-4c77-8940-24b0190768ab/grid_0', w: 1024, h: 2048, prompt: `giant futuristic desert city over deep ocean ecosystem, red starry milky way, split screen photography`},
{id: 'fdd3566a-9cfb-4b11-8733-b54827479c27/grid_0', w: 1664, h: 2432, prompt: `ocean ecosystem, giant futuristic desert city, red starry milky way, split screen photography`},
{id: 'c43a2093-07df-4ea6-866a-f1204ed7aa4b/grid_0', w: 2304, h: 1536, prompt: `mystical connected forest villages`},
{id: 'ea74fc47-dacd-4ddd-afb0-8b9a30589272/grid_0', w: 2304, h: 1536, prompt: `blindfolded woman, golden river of time, intricate ornaments, 8k render`},
{id: 'e8bfe061-f510-4269-90f9-e789b4d29209/grid_0', w: 2048, h: 2048, prompt: `baby wrapped in cabbage leaves`},
{id: 'c90dde4f-47bd-41bc-9574-2a85a5b51a52/grid_0', w: 1024, h: 1536, prompt: `beautiful focaccia vegetable pattern, seamlessly repeating`},
{id: 'fda63c22-f6ff-4ad8-bee2-2e197fbc0a97/grid_0', w: 2048, h: 1024, prompt: `kid's back, low angle, staring up at floating temple in ancient china`},
{id: '7d2393c5-6785-40db-bbea-19fa78b564ec/grid_0', w: 2048, h: 1024, prompt: `kid's back, low angle, staring up at mobius spaceship landing in ancient china`},
{id: '96ea994a-9356-4b2a-ae2a-debfc8d9f5be/grid_0', w: 1664, h: 2432, prompt: `futuristic red & gold demon samurai mask resting on grass, highly detailed, exquisite decoration, symmetric, 8k render`},
{id: '135a44e0-b790-4db7-b10c-084588fc12bd/grid_0', w: 1664, h: 2432, prompt: `futuristic demon samurai mask in glass display, highly detailed, exquisite decoration, symmetric, 8k render`},
{id: '33341a99-5ac6-44df-8812-a819b9a2ff07/grid_0', w: 1664, h: 2432, prompt: `futuristic demon samurai mask, highly detailed, exquisite decoration, symmetric, 8k render`},
{id: 'f637833b-9a01-4d26-9ada-8501fa4c2802/grid_0', w: 2048, h: 2048, prompt: `beautiful iOS health schedule app icon, saturated`},
{id: '25c83d8f-8186-423f-aff3-f8ee0e0e8c5c/grid_0', w: 1024, h: 1024, prompt: `intricate anthropomorphic branches compass from fairy tales`},
{id: '591259df-d807-40f9-92f7-36f2b74d2418/grid_0', w: 1024, h: 1024, prompt: `intricate magical compass from fairy tales`},
{id: 'b6fa5204-940c-474a-b345-e329f8f961b1/grid_0', w: 1024, h: 1536, prompt: `medieval squirrel outfit, pointillism`},
{id: '46eba778-bd08-4144-80d9-48f7646aad01/grid_0', w: 1536, h: 1024, prompt: `paper mario flower shop stage montage`},
{id: '35c8ec01-5884-49ed-8b41-b70710355bfc/grid_0', w: 1536, h: 1024, prompt: `paper mario train station stage, decorated`},
{id: '68016c33-b0b6-4a0c-839b-c09d14119c39/grid_0', w: 2304, h: 1536, prompt: `elegant Roger Federer, red and white`},
{id: '5714f9fb-2672-4e9e-a794-19926e047b06/grid_0', w: 2048, h: 2048, prompt: `yellow and red maples pattern, volumetric lighting, octane renderer, tilt shift`},
{id: '0ed3fce9-6eda-4d1c-b559-b77bd4444a08/grid_0', w: 1024, h: 1024, prompt: `roger federer, red and white, high contrast`},
{id: '9e3baa7d-e892-4e4a-892e-84fd654a06cb/grid_0', w: 1664, h: 2432, prompt: `mirror reflection of a little boy crying`},
{id: '2db19bdc-ec21-4a68-8dba-172212933841/grid_0', w: 2048, h: 2048, prompt: `flying fish`},
{id: '5c196f49-6572-40b9-8529-16350d9cfc13/grid_0', w: 2048, h: 1024, prompt: `5 centimeters per second, toy renderer, 3D cel shading`},
{id: '596808d3-8550-497b-bbf7-e16df6ba9a43/grid_0', w: 2048, h: 1024, prompt: `5 centimeters per second, unity cel shading, curvilinear lens`},
{id: '5c059f02-01ae-4606-8e81-a39df281b97b/grid_0', w: 2048, h: 1024, prompt: `5 centimeters per second, trains, unity cel shading render, fisheye`},
{id: '90a1b52f-5e4f-4d8b-99af-3c5cd59bf434/grid_0', w: 1024, h: 1024, prompt: `irridescent terrine`},
{id: 'e20f7450-a289-44d5-9b83-b5a0b9fedc37/grid_0', w: 2048, h: 1024, prompt: `rainbow cloud bent toward the mountain, makoto shinkai`},
{id: '6b777018-edb8-4a76-be36-8a78652a3199/grid_0', w: 1664, h: 2432, prompt: `the happiest anthropomorphic mushroom dancing in morning dew with her friends, 3D game concept art render, volumetric lighting`},
{id: '3d637738-3996-46d0-b2c3-0af14863322b/grid_0', w: 1408, h: 2816, prompt: `the happiest mushroom growing in the morning dew with her friends, 3D game concept art render, volumetric lighting`},
{id: 'a437c15a-5e3e-42b7-9299-3b1188b40020/grid_0', w: 2048, h: 1024, prompt: `galaxy rainbow, high resolution fisheye lens, long exposure photography`},
{id: '60d99de5-dd01-4f3d-870d-2f957a409d91/grid_0', w: 2304, h: 1536, prompt: `confucius performing the miracle of turning water into boba in front of his family, chinese ink painting`},
{id: '81600ebb-7d32-4660-a3ec-34c6684926a9/grid_0', w: 1024, h: 1024, prompt: `wormhole portal to moon in a mirror, god rays`},
{id: '932eae0c-ed04-4616-8750-e8d29d4cc78b/grid_0', w: 2304, h: 1536, prompt: `silver filligree pearl-encrusted tissue box, intricate details, resting on purple velvet fabric, 8k macrophotography`},
{id: 'fcb1286b-fc40-46bb-a3ce-51bed7232622/grid_0', w: 1024, h: 1536, prompt: `daft punk style mirror helmet reflecting planet earth, man in tuxedo looking down against a galaxy background, dynamic angle`},
{id: '3dae2556-b910-4a9c-8c41-0578472b8e7f/grid_0', w: 2048, h: 2048, prompt: `white tshirt with words "I heart MJ"`},
{id: 'cb5f8b4c-e6dc-4bc0-827f-c330093f2827/grid_0', w: 1664, h: 2432, prompt: `peacock feather eyed human portrait, dynamic angle, extremely detailed 8k`},
{id: 'da3cc4f1-2024-40fd-83b6-36c41fa6ca66/grid_0', w: 2304, h: 1536, prompt: `silver filligree pearl-encrusted tissue box, intricate details, resting on purple velvet fabric, 8k macrophotography`},
{id: 'dc2ca067-e202-49d3-ac0c-b60f14ea5b3d/grid_0', w: 2048, h: 1152, prompt: `magnificent gilded palace hallway, high ornamented ceiling, tall murals with display artifacts, luxurious crimson ornaments, extremely detailed, golden volumetric lighting, well lit, 8k render, low angle, sumptuous scale`},
{id: '05447686-85b8-49a3-8ed7-d3ee5b0ebe0c/grid_0', w: 1792, h: 1024, prompt: `Lu Bu riding his motorcycle into the battlefield, traditional chinese painting`},
{id: '6228d5ef-c902-484a-a8b5-4cfc696bd07d/grid_0', w: 1024, h: 1792, prompt: `warrior Lu Bu in slick corporate suit closing a real estate deal, low angle`},
{id: '4bc25b4f-87e2-432b-b0ff-ed4cbab67951/grid_0', w: 1536, h: 1024, prompt: `art nouveau bento lunch`},
{id: '06b16ea2-3f09-45fe-b4ab-66db853f4583/grid_0', w: 2304, h: 1536, prompt: `colorful vibrant micro ecosystem growing on an iphone`},
{id: 'f14eb017-5655-4013-8b9f-b22c3edf35d8/grid_0', w: 1536, h: 2688, prompt: `art nouveau iOS springboard square icons grid interface`},
{id: '52e76a58-1d26-455f-92d9-d79f50931453/grid_0', w: 2048, h: 2048, prompt: `art nouveau ios icon, detailed, square, floral, 3D render`},
{id: '4408292b-2828-40cf-a81d-9a186a43e53e/grid_0', w: 2304, h: 1664, prompt: `real buildings on a giant monopoly board table, cards, hyperrealistic, tilt shift`},
{id: 'fbd2c6b6-eb92-4ce8-89e9-c0e040296c4c/grid_0', w: 1664, h: 2432, prompt: `audrey hepburn in female darth vader suit`},
{id: 'f951cb42-775f-4e54-be98-58e651e18d30/grid_0', w: 1664, h: 2432, prompt: `audrey hepburn in female vader suit, elegant pose, pipe saber`},
{id: '4f17d6e2-fd63-484b-977d-e27916d5d5d1/grid_0', w: 1664, h: 2432, prompt: `audrey hepburn in female vader suit, elegant pose, pipe saber`},
{id: 'f73be0ac-6c25-4b7a-a878-8fe13a6d69d9/grid_0', w: 2048, h: 2048, prompt: `pikachu in a gacha ball`},
{id: 'e3542b15-1f1c-446a-8f3b-1418760477c0/grid_0', w: 2048, h: 2048, prompt: `pikachu in a gacha ball`},
{id: 'ed43f1b8-88fb-43eb-b0cc-7e785ecb6b41/grid_0', w: 1024, h: 1792, prompt: `sagrada familia corn`},
{id: 'e59e0649-140e-4c6a-aa91-4697dd402729/grid_0', w: 1024, h: 1792, prompt: `tetris world :: tetris T block, octane renderer, god ray, sky`},
{id: '719c4930-70c3-4855-b212-8f8e14c4c3c0/grid_0', w: 1024, h: 1792, prompt: `hyperrealistic tetris blocks dropping down in grid formation, octane render, bottom-up perspective, sky, god ray`},
{id: '8ea1568b-2dca-4279-96e3-2d52fd276597/grid_0', w: 2048, h: 1024, prompt: `river of flamey hair, faded colored manga, xxxholic style`},
{id: 'ad4ad020-52d1-4305-ad23-306647acc495/grid_0', w: 1536, h: 1024, prompt: `giant mosquito queen sitting relaxing near incense, xxxholic style`},
{id: '43d51329-276e-4260-8bd5-53220ff4b023/grid_0', w: 1536, h: 1024, prompt: `iridescent damascus steel clover`},
{id: '884ff494-0be6-44ad-96c6-a7504cbfbd50/grid_0', w: 1536, h: 1024, prompt: `iridescent damascus steel leaf`},
{id: '3781e6d1-a1d5-42e9-b490-49c0981d2aab/grid_0', w: 1792, h: 1024, prompt: `festival, colorful smokes, giant balls, dark blue sky, dancing people in colorful flowing clothes, chaotic dreamy`},
{id: '30d2ccee-488e-47ff-b9b6-17f1997faa10/grid_0', w: 2304, h: 1536, prompt: `amigurumi baby shark in a woven basket, autumn forest morning, dust particle shining in sunlight, high resolution, macrophotography`},
{id: 'c3e0d7e3-cf2d-471c-ae00-94ab4813d312/grid_0', w: 1664, h: 2432, prompt: `quiet garden path, woman sitting in a chair, another empty chair`},
{id: '7f740b3c-86d4-42ba-91c8-230da6ab348d/grid_0', w: 1024, h: 1024, prompt: `united starbucks of america`},
{id: 'b1c0c023-658a-4a9d-aae0-885014704bbf/grid_0', w: 1664, h: 2432, prompt: `portrait of a smiling young asian woman, double eyelids, big eyes, long brown hair with side swept bang, round face, optimistic smile, thin yellow headband, white shirt with lightblue sailor collar`},
{id: '16f75d7e-4308-4f66-b910-7a6a0ca3329e/grid_0', w: 1664, h: 2432, prompt: `jeff bezos as one punch man, shiny bald head, epic, powerful, high resolution hyperrealistic`},
{id: 'c7991998-c193-47c0-bc28-96c9de7b2d4f/grid_0', w: 1664, h: 2432, prompt: `portrait of a young asian woman, double eyelids, big eyes, long brown hair with side swept bang, heart-shaped face, optimistic smile, wearing a yellow head band, wearing a white shirt with lightblue sailor collar`},
{id: '0bde6126-2b0d-4e43-9d6a-99c678599ffa/grid_0', w: 1664, h: 2432, prompt: `portrait of a young asian woman, double eyelids, big downturn eyes, long brown hair with side bang, heart-shaped face, optimistic smile, wearing a yellow head band, wearing a white shirt with blue sailor uniform collar`},
{id: '47809a3c-92e1-4584-ae48-e0addb59a7ba/grid_0', w: 1664, h: 2432, prompt: `older Steve Jobs, long beard, wrinkled face, confident weary smile, monochrome hyperrealistic portrait`},
{id: '9d0769cc-09f3-4ca2-8614-c9a4eabb13be/grid_0', w: 1664, h: 2432, prompt: `older Steve Jobs, long beard, wrinkled face, confident weary smile, monochrome hyperrealistic portrait`},
{id: '0ae13cc6-e11f-4d8d-9521-64bfd76c8797/grid_0', w: 1664, h: 2432, prompt: `KFC double down, 8 layers of fried chicken, 7 layers of bacon and cheese`},
{id: '9fe01d22-c281-4f1a-bb5c-5fa22066900e/grid_0', w: 1024, h: 1024, prompt: `little red riding gundam`},
{id: '734f2a21-cdbc-4c54-b832-0b4206879f42/grid_0', w: 1024, h: 1536, prompt: `lightning waterfall from heaven to earth river, hyperrealistic render`},
{id: '153a8b3e-59dc-4b92-89ab-640bf683af76/grid_0', w: 1024, h: 1024, prompt: `trump burger, satirical comic style`},
{id: '73e9cb44-fa63-4198-a9b3-cd4004f91654/grid_0', w: 1536, h: 1024, prompt: `giant robot mecha, Ukiyo-e hokusai`},
{id: '6fea9fd0-a4f0-4b79-886b-ec1ecba3ee81/grid_0', w: 1024, h: 1536, prompt: `giant robot mecha, Ukiyo-e hokusai`},
{id: 'a248d49c-749d-49f8-a114-32d809d3d496/grid_0', w: 2048, h: 1024, prompt: `flight of paper dragons, tolkiens, rendering`},
{id: 'cc56ea37-74ca-4ca8-bfc1-96bfc8e5a383/grid_0', w: 1024, h: 1792, prompt: `smoke bonsai sculpture, hilltop, macrophotography, 8k octane render`},
{id: 'e08b34f6-d03d-4f1e-aa05-e6a1120e5000/grid_0', w: 1024, h: 1536, prompt: `smoke sculpture maiden silhouette, 8k octane render`},
{id: '1b843b1d-2451-42ed-a933-76817ef2b736/grid_0', w: 1024, h: 1536, prompt: `marble smoke sculpture maiden silhouette, 8k octane render`},
{id: '8f0e0675-ac0a-4615-b222-934de426edc7/grid_0', w: 1024, h: 1024, prompt: `origami crane made of concrete`},
{id: 'cdc70000-2af2-42c3-9db3-86fe266ebb0a/grid_0', w: 1280, h: 1024, prompt: `mapo tofu, fake toy rendering, closed-up diorama, hyperrealistic 8k`},
{id: '76faee1f-354d-4420-b6d9-39719dbb8d20/grid_0', w: 1024, h: 1792, prompt: `thick yellow fog invading a small winter town in the morning, yellow sky, diorama`},
{id: 'e2ce8a73-ab2d-4540-9732-fc4f528c0ca8/grid_0', w: 1792, h: 1024, prompt: `thick yellow fog invading a small town in winter, diorama`},
{id: '2a6be313-344d-40ff-b750-ee6e044fafb8/grid_0', w: 1024, h: 1536, prompt: `melting black vinyl on a modern minimalistic white turntable, 8k octane render`},
{id: 'ce63122a-5285-417a-9816-b5425c0ff6a8/grid_0', w: 1024, h: 1536, prompt: `melting vinyl on a modern pearl white turntable, 8k octane render`},
{id: '3801b82d-294c-4f3f-9c01-68290aed8c37/grid_0', w: 1024, h: 2048, prompt: `coke sirup dripping down icicle in winter, 45mm film`},
{id: '6a02df80-6058-4cd9-b583-88f0ede64551/grid_0', w: 1536, h: 1024, prompt: `kimono omakase`},
{id: '6898bb0e-2f05-4a41-8bfb-4250a66d8869/grid_0', w: 2304, h: 1536, prompt: `white seashell shaped apartment interior, light wood floor, futuristic, warm, rendering`},
{id: '32301d78-6ea6-43af-a095-08eb0f7575e8/grid_0', w: 1536, h: 1024, prompt: `white seashell residential apartments, futuristic, 3/4 perspective, photorealistic`},
{id: '536b3830-d144-470b-8c6e-f25f947edc8e/grid_0', w: 1792, h: 1024, prompt: `swarm of ladybugs flying toward the Milky Way, bottom up perspective, expressionism, Van Gogh`},
{id: '883264da-082c-484c-9eba-ecd0553df0f9/grid_0', w: 1024, h: 1024, prompt: `straw castle`},
{id: 'fc444028-779c-44f6-ac74-08fb62e5469a/grid_0', w: 1024, h: 1280, prompt: `chinese new year on Neptune, in the style of Salvador Dali`},
{id: '515684a7-715c-48cf-b330-3595fb26e1de/grid_0', w: 1792, h: 1024, prompt: `dandelion boat`},
{id: '0c2c63d0-3749-4ae5-998d-7d3338b98cb9/grid_0', w: 1536, h: 2688, prompt: `koi silk`},
{id: '901578e9-79a1-43fc-a939-b7c04f5acc0e/grid_0', w: 1536, h: 2688, prompt: `koi silk`},
{id: 'bd00d75a-8c11-46bd-a1c6-b43f35a13d5f/grid_0', w: 2048, h: 1152, prompt: `they're not mountains; they're waves!`},
{id: 'fc0e3851-6c92-4181-892b-88cadcce6823/grid_0', w: 1024, h: 1024, prompt: `Apple logo if Tim Cook drew it`},
{id: 'e35f28a6-4dfc-4248-851b-8d60cfc6b676/grid_0', w: 1792, h: 1024, prompt: `detailed schematic of instructions on using a pencil`},
{id: '3205212f-e5d7-44ba-a2e2-e72d44311e4f/grid_0', w: 1024, h: 1024, prompt: `meiji era painting of a banana katana, ink`},
{id: '9f184005-9007-4a93-ab6f-49df2a56d8b7/grid_0', w: 1792, h: 1024, prompt: `Asian farmer kid sitting on old pavement, books on a white sheet, old grainy photo`},
{id: '5a78f6fe-efea-485d-b0e6-b87c39a50b81/grid_0', w: 1792, h: 1024, prompt: `spinning space station, in the style of Christopher Nolan, 65mm film`},
{id: '6f8b0f56-1b88-449c-96e2-bd91d4344cdb/grid_0', w: 1792, h: 1024, prompt: `Asian farmer kid casting a fishing net, in a muddy rice field, sunrise`},
{id: '184a98ed-291c-4c27-a711-c8be863095b6/grid_0', w: 1536, h: 1024, prompt: `glistening gold coins in a dull dark blue trash bag in a dumpster`},
{id: 'e5fc9b09-dc4e-4410-8bf8-0a03bcfa9235/grid_0', w: 1024, h: 1024, prompt: `watermelon grape, photo`},
{id: '693d46f6-0f48-4578-9bb3-8c252a931f8c/grid_0', w: 1536, h: 1024, prompt: `grainy monochrome photo of an interstellar spaceship in 1950, high resolution`},
{id: 'a0101172-7c41-4e19-9c64-53442943eb12/grid_0', w: 1024, h: 1024, prompt: `withered flowers inside a coke bottle`},
{id: '175ba7de-87f5-4620-9589-fa3a509f9c61/grid_0', w: 2048, h: 1024, prompt: `closed up gold and green leaf pattern on a fancy gate door, 8k depth of field`},
{id: '12e9f165-9e0f-4773-8e11-54209e5c02c7/grid_0', w: 1024, h: 2048, prompt: `macrophotography of flower pattern sculpture, bas-relief, 8k octane renderer`},
{id: '29bc75c5-65f2-448f-9c6c-560c9e439ed5/grid_0', w: 1024, h: 2048, prompt: `macrophotography of flower pattern sculpture, bas-relief, 8k octane renderer`},
{id: '62174480-a054-4f49-a278-76b90744afd4/grid_0', w: 2688, h: 1536, prompt: `sea of fog over cherry blossom, tilt shift photography`},
{id: 'c346fa1b-fc5f-49c8-960e-5156947e29bd/grid_0', w: 1792, h: 1024, prompt: `sea of fog over cherry blossom, tilt shift photography`},
{id: '7a9b5e5b-7ef2-49d1-8664-a917cce1f784/grid_0', w: 1792, h: 1024, prompt: `sushi made of stone, 8k octane renderer`},
{id: 'cfd9b144-dc17-40fe-aab7-3b7bb6d6b52a/grid_0', w: 1920, h: 1280, prompt: `impossible sushi`},
{id: 'b317c31b-185f-457a-8f09-c8d088f4c9dd/grid_0', w: 1792, h: 1024, prompt: `photo of snowman on fire, depth of field, 8k`},
{id: '64f077d2-ded9-4291-8116-2a7e4edd74f5/grid_0', w: 1792, h: 1024, prompt: `photo of snowman on fire, depth of field, 8k`},
{id: 'e987bfc2-0a52-46b9-add6-9537518398d5/grid_0', w: 1024, h: 1024, prompt: `storm in a teacup, diorama`},
{id: '586f0bf8-639e-401c-8622-33d1e9736cdb/grid_0', w: 1024, h: 1024, prompt: `storm in a teacup, origami`},
{id: '0f3ea9a2-ad79-491c-87db-3bf7d7bb0547/grid_0', w: 1024, h: 1024, prompt: `the word "ReScript", clear font, perfect kerning`},
{id: '9c0a4fea-ae34-4c07-8e77-4aa7a9d5bdc5/grid_0', w: 2304, h: 1024, prompt: `the four bunnies of apocalypse, dystopian, artstation, high quality render`},
{id: '9826680f-5cd4-4548-9a39-60eb091e58f2/grid_0', w: 1024, h: 1024, prompt: `infinity gauntlet spaceship`},
{id: 'af2a7c0a-be8f-458d-a582-6c5a0f00e406/grid_0', w: 2048, h: 2048, prompt: `infinity gauntlet spaceship`},
{id: 'a9b5bd52-5989-42ef-b6f1-90ff75201727/grid_0', w: 2304, h: 1024, prompt: `city made of human internal parts, bird eye view`},
{id: '05562c53-fe19-4ec6-a3df-8f1e3f507fc7/grid_0', w: 1024, h: 1024, prompt: `liver organ doing karate, Impressionism`},
{id: '9105e83a-4bf4-4cad-a002-091b6c2e807c/grid_0', w: 1024, h: 1024, prompt: `baby bear thanos`},
{id: '28b40b6b-2c31-4f89-b9aa-cbeeed53f899/grid_0', w: 1792, h: 1024, prompt: `one wooden rocket with metal plates fixtures, children crayon drawing on paper`},
{id: '815449fd-8fc5-4991-964b-93351a0f6826/grid_0', w: 2048, h: 2048, prompt: `steampunk fish`},
{id: '08e8ca60-e330-4a0f-b677-ccf7e5f17bc1/grid_0', w: 2048, h: 2048, prompt: `pearl necklace on red dress, 8k sculpture, cinematic, octane renderer`},
{id: '5c32d5f8-861c-493f-b98e-4fb0a4734d7a/grid_0', w: 1792, h: 1024, prompt: `floating house on a lake, photorealistic, peaceful`},
{id: '554959ca-cdfc-4013-bbdf-98fac4b595e3/grid_0', w: 1792, h: 1024, prompt: `macrophotography of butterfly with flower petal wings, abstract`},
{id: 'ec0a09bd-c053-47a3-804d-5b46a960a964/grid_0', w: 1280, h: 1024, prompt: `do not go gentle into that good night`},
{id: 'aba2dcec-9852-41c5-af2a-4e56d356306d/grid_0', w: 2048, h: 1152, prompt: `dreamy crystal butterfly, shifted perspective`},
{id: 'cf77943e-dc69-49cb-897e-4efc9b3d4cc6/grid_0', w: 1664, h: 1664, prompt: `crystal butterfly in a dream`},
{id: '9c8420b0-4a42-45c9-8d87-414a89f7b304/grid_0', w: 1152, h: 2048, prompt: `eclipse in a bottle`},
{id: '49fc4fed-d20b-4706-b1fb-2f40eaa39c83/grid_0', w: 1536, h: 2688, prompt: `eclipse in a bottle`},
{id: 'fb422f6e-d87a-45a7-b6e8-1b8f70f2bd61/grid_0', w: 1664, h: 1664, prompt: `old stamp of a futuristic city in birds eye view, instagram filter`},
{id: '88be2db6-c39f-436b-89c5-753698855fc6/grid_0', w: 1024, h: 1024, prompt: `old stamp of a futuristic city in birds eye view, instagram filter`},
{id: '6154dfaf-bbf1-4bec-bf4c-d4f2825fc103/grid_0', w: 2304, h: 1024, prompt: `iphone inside a capped glass bottle lying horizontally on an old table`},
{id: '74db9b64-2af6-4499-bfea-09b76ff8eedc/grid_0', w: 2304, h: 1024, prompt: `futuristic solarpunk city, bird's eyes view, traditional red asian buildings wrapped in green vegetations, unreal engine`},
{id: 'ea29a59f-2c0c-43d3-bcea-9c080d1b78ea/grid_0', w: 1536, h: 1536, prompt: `golden retriever puppy wearing a helmet, against a beautiful galaxy, octane renderer`},
{id: 'e69d238a-4d71-4fe3-9f4b-5a3ac471f5b2/grid_0', w: 1664, h: 1664, prompt: `pretty dress blowing in the wind`},
{id: '0417c327-5eac-441e-923f-9736dcd8e380/grid_0', w: 1664, h: 1664, prompt: `closed up macrophotography of a bowl of asian dessert`},
{id: '0740ff12-0b35-4905-8dad-bf550a84cb27/grid_0', w: 1024, h: 1024, prompt: `closed up macrophotography of a bowl of asian dessert`},
{id: '8aa7fd74-73ea-4831-a48a-ce9edc67d414/grid_0', w: 2048, h: 1152, prompt: `asian girl in straw hat and white dress, clear sunny sky, sunflower field, portrait photography`},
{id: '8ee2a580-a6a1-4356-81d1-40e9bc01f5e2/grid_0', w: 1024, h: 1024, prompt: `so much depends upon a red wheel barrow glazed with rain water beside the white chickens`},
{id: 'e21ccdd0-4efe-4058-b176-d96c6c673642/grid_0', w: 1408, h: 1792, prompt: `three-body problem, cinematic`},
{id: '19a138c9-8d56-471f-bc08-351fda122b5e/grid_0', w: 1408, h: 1792, prompt: `three-body problem, cinematic`},
{id: '14465d26-a41e-4cc7-9a70-c7de8a0e28b3/grid_0', w: 1408, h: 1792, prompt: `three-body problem, cinematic`},
].map((d, i) => {
const ar = d.w / d.h
const sizeX = Math.min(d.w, boxMaxSizeX, imgMaxSizeY * ar)
const sizeY = sizeX / ar + promptSizeY
// upon zooming into 1D mode (big image), swapping out an img src for a higher-res one would cause a flash of blank image in certain cases. Instead, we put the low-res image as a background-image on the container, then the high-res image as a real img on top. Hand-rolled double buffering...
let node = document.createElement('div')
node.className = 'box'
// node.tabIndex = i + 1 // uncomment when dismiss focus isn't this ugly blue hue anymore
node.style.backgroundImage = `url(https://cdn.midjourney.com/${d.id}_384_N.webp)` // 128 is the next smallest. Too small for retina screens
let img = document.createElement('img')
// img.decoding = 'async' // this sucks. It's slower _and_ still janks the UI. No point
let promptNode = document.createElement('figcaption')
promptNode.className = 'prompt'
promptNode.textContent = d.prompt
node.append(img, promptNode)
return {
id: d.id,
naturalSizeX: d.w,
ar, // aspect ratio
sizeX: spring(sizeX),
sizeY: spring(sizeY), // image + prompt
x: spring(Math.floor(i / cols) * -windowSizeX - windowSizeX), // unfold from lower left. More visible on long screens
y: spring(windowSizeY + Math.floor(i / cols) * imgMaxSizeY),
scale: spring(1),
fxFactor: spring(20), // for brightness and blur
node,
}
})
}
function springForEach(f) { // no spring ownership struggle between the spring library above vs consumer; un-inversion of control!
for (let d of data) {
f(d.sizeX); f(d.sizeY); f(d.x); f(d.y); f(d.scale); f(d.fxFactor) // no different than [a, b, c].forEach(f)
}
}
// === events
// pointermove doesn't work on android, pointerdown isn't fired on Safari on the first left click after dismissing context menus, mousedown doesn't trigger properly on mobile, pointerup isn't triggered when pointer panned (at least on iOS), don't forget contextmenu event. Tldr there's no pointer event that works cross-browser that can replace mouse & touch events.
window.addEventListener('resize', () => scheduleRender())
window.addEventListener('scroll', () => scheduleRender(), true) // capture is needed for iPad Safari...
window.addEventListener('popstate', () => scheduleRender())
window.addEventListener('keydown', (e) => {events.keydown = e; scheduleRender()})
window.addEventListener('click', (e) => {events.click = e; scheduleRender()})
window.addEventListener('mousemove', (e) => {events.mousemove = e; scheduleRender()})
// === static DOM initialization. Just 1 in this app. The more you have here the more your app looks like a PDF document. Minimize
let dummyPlaceholder = document.createElement('div')
dummyPlaceholder.style.position = 'absolute'
dummyPlaceholder.style.width = '1px' // make it tiny in case it affects compositing... sigh lamport.azurewebsites.net/pubs/future-of-computing.pdf
document.body.append(dummyPlaceholder)
if (debug) {
document.documentElement.style.background = 'repeating-linear-gradient(#e66465 0px, #9198e5 300px)'
document.documentElement.style.height = '100%'
}
// === hit testing logic. Boxes' hit area should be static and not follow their current animated state usually (but we can do either)
function hitTest2DMode(data, pointerX, pointerY) {
for (let i = 0; i < data.length; i++) {
let {x, y, sizeX, sizeY} = data[i]
if (x.dest <= pointerX && pointerX < x.dest + sizeX.dest && y.dest <= pointerY && pointerY < y.dest + sizeY.dest) return i // pointer on this box
}
return null
}
function hitTest1DMode(data, focused, windowSizeX, pointerX) {
// allow spamming clicks without accidentally clicking on an empty region on left/right side or newly focused image during transition (and dismiss 1D mode)
return focused > 0 && 0 <= pointerX /* might be -Infinity */ && pointerX <= hitArea1DSizeX ? Math.max(0, focused - 1) // left
: focused < data.length - 1 && pointerX >= windowSizeX - hitArea1DSizeX ? Math.min(data.length - 1, focused + 1) // right
: null
}
function render(now) {
// === step 0: process events
// keydown
const inputCode = events.keydown == null ? null : events.keydown.code
// click
let clickedTarget = null
if (events.click != null) {
// needed to update coords even when we already track mousemove. E.g. in Chrome, right click context menu, move elsewhere, then click to dismiss. BAM, mousemove triggers with stale/wrong (??) coordinates... Click again without moving, and now you're clicking on the wrong thing
clickedTarget = events.click.target
pointer.x = events.click.clientX; pointer.y = events.click.clientY
}
// mousemove
if (events.mousemove != null) {
// we only use clientX/Y, not pageX/Y, because we want to ignore scrolling. See comment around isSafari above; we either scroll body or window depending on the browser, so pageX/Y might be meaningless (if Safari)
pointer.x = events.mousemove.clientX; pointer.y = events.mousemove.clientY
// btw, pointer can exceed document bounds, e.g. dragging reports back out-of-bound, legal negative values
}
if (debug) {
if (inputCode === 'KeyA') debugTimestamp += 1000 / 60
now = debugTimestamp
}
// === step 1: batched DOM reads (to avoid accidental DOM read & write interleaving)
const newWindowSizeX = document.documentElement.clientWidth // excludes scroll bar & invariant under safari pinch zoom
const windowSizeY = document.documentElement.clientHeight // same
// this way, when pinch zooming in, we don't occlude away rows outside of the view; if we did, when we zoom out again we wouldn't see those occluded rows until we release our fingers. During safari pinch, no event is triggered so we couldn't have updated the occlusion in real time
const animationDisabled = reducedMotion.matches
const currentScrollY = isSafari ? document.body.scrollTop : window.scrollY
const currentScrollX = isSafari ? document.body.scrollLeft : window.scrollX
const hashImgId = window.location.hash.slice(1)
let focused = null; for (let i = 0; i < data.length; i++) if (data[i].id === hashImgId) focused = i
// don't forget top & bottom safari UI chrome sizes when vertically occluding, since they're transluscent so we can't over-occlude by ignoring them
const pointerXLocal = pointer.x +/*toLocal*/currentScrollX, pointerYLocal = pointer.y +/*toLocal*/currentScrollY
// === step 2: handle inputs-related state change
// keys
let newFocused =
inputCode === 'Escape' ? null
: (inputCode === 'ArrowLeft' || inputCode === 'ArrowRight') && focused == null ? 0
: inputCode === 'ArrowLeft' ? Math.max(0, focused - 1)
: inputCode === 'ArrowRight' ? Math.min(data.length - 1, focused + 1)
: focused
// pointer
if (clickedTarget != null) { // clicked
if (clickedTarget.tagName === 'FIGCAPTION') { // select the whole prompt
let selection = window.getSelection()
let range = document.createRange()
range.selectNodeContents(clickedTarget)
selection.removeAllRanges()
selection.addRange(range)
} else if (focused == null) { // in 2D grid mode. Find the box the pointer's on
newFocused = hitTest2DMode(data, pointerXLocal, pointerYLocal) ?? newFocused
} else { // 1D mode
newFocused = hitTest1DMode(data, focused, newWindowSizeX, pointerXLocal)
}
}
// === step 3: calculate new layout & cursor
const {cols, boxMaxSizeX} = colsBoxMaxSizeXF(newWindowSizeX)
let boxes2DSizeX = [], boxes2DSizeY = [], rowsTop = [windowPaddingTop] // length: number of rows + 1
{ // first pass over data to set final 2D dimensions and row height (for vertical centering in 2D & total scrollbar height)
let rowMaxSizeY = 0
for (let i = 0; i < data.length; i++) {
let d = data[i]
const imgMaxSizeY =
d.ar === 1 ? boxMaxSizeX * 0.85 // square aspect ratio area too big. Shrink it
: d.ar < 1 ? boxMaxSizeX * 1.05 // vertical images look a bit small. Grow it
: boxMaxSizeX
const sizeX = Math.min(d.naturalSizeX, boxMaxSizeX, imgMaxSizeY * d.ar)
const sizeY = sizeX / d.ar + promptSizeY
boxes2DSizeX.push(sizeX)
boxes2DSizeY.push(sizeY)
rowMaxSizeY = Math.max(rowMaxSizeY, sizeY)
if (i % cols === cols - 1 || i === data.length - 1) { // last box of the row or last box ever
rowsTop.push(rowsTop.at(-1) + rowMaxSizeY + boxesGapY)
rowMaxSizeY = 0
}
}
}
let cursor
let newAnchor = anchor
let adjustedScrollTop = currentScrollY
const hoverMagnetFactor = 40
if (newFocused == null) { // 2D mode
if (focused != null) { // just dismissed 1D mode
const focusedTop = rowsTop[Math.floor(focused / cols)]
// if the dismissed box isn't fully shown, scroll to it (later after the render code block)
if (focusedTop <= currentScrollY || focusedTop + boxes2DSizeY[focused] >= currentScrollY + windowSizeY) {
adjustedScrollTop = focusedTop - boxesGapY - gapTopPeek // Peek a little higher to show the previous row
}
}
for (let i = 0; i < data.length; i++) { // calculate boxes positions
let d = data[i]
const sizeX = boxes2DSizeX[i], sizeY = boxes2DSizeY[i]
const currentRow = Math.floor(i / cols)
const rowMaxSizeY = rowsTop[currentRow + 1] - boxesGapY - rowsTop[currentRow] // this is restoring the rowMaxSizeY info above, kinda weird
d.sizeX.dest = sizeX
d.sizeY.dest = sizeY
d.x.dest = boxesGapX + (boxMaxSizeX + boxesGapX) * (i % cols) + (boxMaxSizeX - sizeX) / 2 // center horizontally
d.y.dest = rowsTop[currentRow] + (rowMaxSizeY - sizeY) / 2
d.scale.dest = 1
d.fxFactor.dest = 1
}
const hit = hitTest2DMode(data, pointerXLocal, pointerYLocal)
if (hit == null) cursor = 'auto'
else { // hovering over a box. Adjust position
cursor = 'zoom-in'
let {x, y, sizeX, sizeY, scale} = data[hit]
x.dest += (pointerXLocal - (x.dest + sizeX.dest / 2)) / hoverMagnetFactor
y.dest += (pointerYLocal - (y.dest + sizeY.dest / 2)) / hoverMagnetFactor
scale.dest = 1.02
}
// if layout shifted, keep the boxes near the same place to prevent annoying layout jumps while viewing
const anchorY = data[anchor].y.dest - gapTopPeek
if (newWindowSizeX !== windowSizeX) adjustedScrollTop = Math.max(0, anchorY) // resized; maintain position!
if (adjustedScrollTop !== scrollY && Math.abs(anchorY -/*toLocal*/adjustedScrollTop) > windowSizeY / 10) { // find new anchor if the current one moved too much
for (newAnchor = 0; newAnchor < data.length; newAnchor += cols) { // new anchor is picked from leftmost column. Btw old anchor might not be from leftmost col due to resize layout shifts
let d = data[newAnchor]
// find 1st box whose bottom exceeds 20% of window height
if (d.y.dest + d.sizeY.dest -/*toLocal*/adjustedScrollTop > windowSizeY / 5) break
}
}
} else { // 1D mode
const img1DSizeY = windowSizeY - windowPaddingTop - prompt1DSizeY - boxes1DGapY
const box1DMaxSizeX = newWindowSizeX - boxes1DGapX * 2 - hitArea1DSizeX * 2
let currentLeft = hitArea1DSizeX + boxes1DGapX // start from the right edge of the left box and...
for (let i = newFocused - 1; i >= 0; i--) { // ...iterate til we get the left edge of the very first box
let d = data[i]
const imgSizeX = Math.min(d.naturalSizeX, box1DMaxSizeX, img1DSizeY * d.ar) * 0.7
currentLeft -= imgSizeX + boxes1DGapX
}
const edgeRubberBandVelocityX = // feedback when you hit first/last image and keep pressing left/right key
inputCode === 'ArrowLeft' && focused === 0 ? 2 * 1000 // 2 pixels per second
: inputCode === 'ArrowRight' && focused === data.length - 1 ? -2 * 1000
: 0
for (let i = 0; i < data.length; i++) { // calculate boxes positions
let d = data[i]
const imgSizeX = Math.min(d.naturalSizeX, box1DMaxSizeX, img1DSizeY * d.ar) * (i === newFocused ? 1 : 0.7)
const boxSizeY = imgSizeX / d.ar + prompt1DSizeY
d.sizeX.dest = imgSizeX
d.sizeY.dest = boxSizeY
d.y.dest = Math.max(windowPaddingTop, (windowSizeY - boxSizeY) / 2) +/*toLocal*/adjustedScrollTop
d.x.dest = i === newFocused ? (newWindowSizeX - imgSizeX) / 2 : currentLeft
d.x.v += edgeRubberBandVelocityX / (i === newFocused ? 1 : 4)
d.scale.dest = 1
d.fxFactor.dest = i === newFocused ? 1 : 0.2 // center image has no brightness & blur effect
currentLeft = i === newFocused ? newWindowSizeX - hitArea1DSizeX : currentLeft + imgSizeX + boxes1DGapX
}
const hit = hitTest1DMode(data, newFocused, newWindowSizeX, pointerXLocal)
if (hit == null) cursor = 'zoom-out'
else { // hovering on left or right image
cursor = 'zoom-in'
let {x, y, sizeX, sizeY, scale, fxFactor} = data[hit]
x.dest += (pointerXLocal - (x.dest + sizeX.dest / 2)) / hoverMagnetFactor
y.dest += (pointerYLocal - (y.dest + sizeY.dest / 2)) / hoverMagnetFactor
scale.dest = 1.02
fxFactor.dest = 0.5
}
}
// ensure that no matter how the scrolling is abruptly adjusted, the boxes on the screen don't suddenly jump too. When going 1D->2D mode where the dismissed image might be far from the initial one, or when resizing causes layout shifts, the boxes now stay unaffected!
for (let {y} of data) y.pos += adjustedScrollTop - currentScrollY
// === step 4: run animation
let newAnimatedUntilTime = animatedUntilTime ?? now
const steps = Math.floor((now - newAnimatedUntilTime) / msPerAnimationStep) // run x spring steps. Decouple physics simulation from framerate!
newAnimatedUntilTime += steps * msPerAnimationStep
let stillAnimating = false
if (animationDisabled) springForEach(springGoToEnd)
else {
springForEach(s => {
for (let i = 0; i < steps; i++) springStep(s)
if (Math.abs(s.v) < 0.01 && Math.abs(s.dest - s.pos) < 0.01) springGoToEnd(s) // close enough, done
else stillAnimating = true
})
}
// === step 5: render. Batch DOM writes
const browserUIMaxSizeTop = 100, browserUIMaxSizeBottom = 150 // browsers UI like Safari are transluscent. Random conservative numbers
for (let i = 0; i < data.length; i++) {
let d = data[i]
let {node} = d, img = node.children[0], prompt = node.children[1]
if ( // occlusion culling, aka only draw what's visible on screen (aka "virtualization")
d.y.pos -/*toGlobal*/adjustedScrollTop <= windowSizeY + browserUIMaxSizeBottom &&
d.y.pos + d.sizeY.pos -/*toGlobal*/adjustedScrollTop >= -browserUIMaxSizeTop &&
d.x.pos <= newWindowSizeX &&
d.x.pos + d.sizeX.pos >= 0
) { // disregard shadow & scaling for now; browserUIMaxSizeBottom and browserUIMaxSizeTop are safe bigger values anyway
node.style.width = `${d.sizeX.pos}px`
node.style.height = `${d.sizeY.pos}px`
node.style.transform = `translate3d(${d.x.pos}px,${d.y.pos}px,0) scale(${d.scale.pos})` // safari now anti-aliases for hover, but then zoom in janks on big displays...
// we can't afford fxFactor & blur for all pics; too expensive for Safari & Chrome. E.g. when zomming out of a photo, keep scrolling; Chrome stops render on Studio Display
node.style.filter = newFocused != null && i === newFocused - 1 || i === newFocused || i === newFocused + 1
? `brightness(${d.fxFactor.pos * 100}%) blur(${Math.max(0, 6 - d.fxFactor.pos * 6)}px)` // blur these 3 only
: `brightness(${d.fxFactor.pos * 100}%)` // blur of unrelated pics is too fast during transition from/to 1D mode to be seen anyway
if (debug) node.style.outline = i === newAnchor ? '2px solid rgba(255, 255, 0, 0.8)' : 'none'
prompt.style.top = `${d.sizeX.pos / d.ar}px` // right below img's sizeY
if (i === newFocused) {
node.style.zIndex = data.length + 999 // guaranteed above everything
prompt.style.overflowY = 'auto'
prompt.style.height = `${prompt1DSizeY - promptPaddingBottom}px`
prompt.style.lineClamp = prompt.style.webkitLineClamp = 999
img.style.display = 'block'
let src = `https://cdn.midjourney.com/${d.id}.webp`
if (!stillAnimating && img.src !== src) img.src = src // load the full res image
} else {
node.style.zIndex = i + 1 // simple proper z-index management
prompt.style.overflowY = 'hidden'
prompt.style.height = `${promptSizeY - promptPaddingBottom}px`
prompt.style.lineClamp = prompt.style.webkitLineClamp = 2
img.style.display = 'none' // hide full res image for perf (yes it makes a difference, even on M1, with Studio Display)
}
if (node.parentNode == null) document.body.appendChild(node) // if previously absent, add
} else if (node.parentNode != null) document.body.removeChild(node) // if previously present, remove
}
document.body.style.cursor = cursor
document.body.style.overflowY = newFocused == null ? 'auto' : 'hidden'
dummyPlaceholder.style.height = `${rowsTop.at(-1)}px` // Chrome has race conditon if scrollTo is called before setting a longer dummy height
// === step 6: update state & prepare for next frame
// if (adjustedScrollTop !== currentScrollTop) window.scrollTo({top: adjustedScrollTop}) // will trigger scrolling, thus next frame's render
if (adjustedScrollTop !== currentScrollY) {
// see comment about isSafari above
(isSafari ? document.body : window).scrollTo({top: adjustedScrollTop}) // will trigger scrolling, thus next frame's render
}
if (newFocused !== focused) {
window.history.pushState(null, '', `${window.location.pathname}${window.location.search}${newFocused == null ? '' : '#' + data[newFocused].id}`)
}
events.keydown = events.click = events.mousemove = null
animatedUntilTime = stillAnimating ? newAnimatedUntilTime : null
anchor = newAnchor
windowSizeX = newWindowSizeX
scrollY = adjustedScrollTop
return stillAnimating
}
scheduleRender()
</script>
</body>
</html>