Bevægelse er et materiale, ikke en effekt.
Fire færdige generative komponenter, hver fra en anden gren af matematikken, hver bygget til at kunne sendes i produktion. Ingen af dem er en demo: de håndterer resize, pixelforhold, synlighed, reduceret bevægelse og pointer, og de fortæller dig selv hvad de koster.
Strømfeltet
Tusinder af partikler der driver gennem et usynligt vektorfelt. Feltet er 3D-værdistøj: to rumlige akser og tiden som den tredje, så mønsteret ikke bare bevæger sig, det forvandler sig. Ingen partikel ved noget om nogen anden. Al struktur du ser, er feltets.
Parametre
Støjen er tredimensionel
Vinklen slås op i noise(x·s, y·s, t·ts). Den tredje akse er tid. Bruger man i stedet 2D-støj og flytter feltet, glider mønsteret hen over skærmen som en tapet, her ændrer det karakter på stedet.
Her er overtegning det rigtige
Sporene laves ved at male et halvtransparent rektangel hen over billedet hver frame. Modsat plade B, hvor hvert segment får sin egen alpha. Forskellen: her er der 4000 spor og ingen der skal se ens ud, så den billige metode er også den pæneste.
Partikler dør med vilje
Hver partikel har en levetid og genfødes tilfældigt. Uden det samler alting sig i feltets attraktorer efter få sekunder, og billedet dør. Levetiden er den vigtigste parameter, du ikke ville have gættet.
Farven kommer fra farten
Ikke fra positionen. Hurtige partikler er lysere. Det gør feltets struktur læsbar uden at man behøver tegne feltet selv, hastighed er allerede information.
Grafen
Et netværk der finder sin egen form gennem frastødning, fjedre og dæmpning, og agenter der ikke vandrer tilfældigt, men beregner den korteste vej fra A til B og løber den. Det er forskellen mellem en pauseskærm og noget der ser ud som om det vil noget. Træk i en node.
Parametre
Topologien er ikke tilfældig
Netværket bygges med præferentiel tilknytning: en ny node forbinder sig helst til dem, der allerede har mange forbindelser. Det giver knudepunkter. Et uniformt tilfældigt netværk ser dødt ud, fordi virkelige netværk aldrig er uniforme.
Tre kræfter, ikke én
Frastødning mellem alle par (O(n²), fint under ~300 noder), fjedre langs kanterne mod en hvilelængde, og en svag centrering så grafen ikke driver væk. Dæmpningen afgør om den svinger eller sætter sig.
Agenterne beregner en rute
Dijkstra med kantlængde som vægt, kørt én gang når agenten fødes. Derefter følger den bare sin liste af noder. Ruteberegningen er gratis; det er tegningen der koster.
Energien fortæller dig hvornår
Målet i baren er den samlede kinetiske energi. Falder den under en tærskel, er layoutet sat, og man kan holde op med at simulere. I produktion er dét dit signal til at fryse, ikke en timer.
Diffusionen
Gray–Scott: to stoffer der diffunderer med hver sin hastighed, reagerer med hinanden, og som ud af en jævn suppe frembringer pletter, striber og celledeling. To ligninger. Ingen regler om form. Tegn på den med musen, den vokser videre af sig selv.
Parametre
To tal styrer alt
feed og kill. Hele RAMTT af mønstre, pletter, orme, labyrinter, evig celledeling, ligger i et lille område af det todimensionelle rum de udspænder. Flyt dem en tusindedel og du er i et andet regime.
Den regner i småt og vises i stort
Simuleringen kører på et gitter på nogle få tusinde celler, skrives med putImageData til et lille canvas, og skaleres op med drawImage. Browserens skalering er gratis; at regne i fuld opløsning er det ikke.
To buffere, aldrig én
Hvert trin læser fra den ene og skriver til den anden, hvorefter de byttes. Skriver man tilbage i den man læser fra, forplanter fejlen sig gennem gitteret inden for én frame og mønsteret bliver retningsbestemt.
Det er her 2D canvas knækker
Alt andet på siden er lineært i antal elementer. Denne er kvadratisk i opløsning og ganges med antal trin. Se den målte kurve nedenfor, det er den eneste af de fire, der har et reelt loft.
Tilstandsfeltet
De tre første er smukke. Denne er brugbar. Samme komponent, to forskellige datastrømme: bevægelsen er ikke dekoration, den er aflæsningen. Amplitude følger niveauet, uroen følger volatiliteten, farven følger zonen. Send et spike ind og se hvordan den reagerer, og hvor længe den er om det.
Parametre
Glat signalet, ikke billedet
Rådata hopper. En EWMA med en tidskonstant du kan skrue på ligger mellem data og visual. Uden den flimrer komponenten ved hver måling, og brugeren lærer at ignorere den.
Hold en rolig grundtilstand
Hvis komponenten altid er i bevægelse, betyder bevægelse ingenting. Reservér intensiteten. Det meste af tiden skal den være næsten stille, så er den værd at kigge på når den ikke er.
Farve er en zone, ikke en gradient
Tre diskrete tilstande med bløde overgange slår en kontinuerlig farveskala. Folk kan aflæse "gul" fra øjenkrogen; de kan ikke aflæse 63 % hen ad en rampe.
Reduceret bevægelse må ikke fjerne information
Slår man bevægelsen fra, skal aflæsningen stadig være der, her som en statisk profil. En komponent hvor prefers-reduced-motion gør skærmen tom, bar information i bevægelsen alene. Det er en fejl, ikke en indstilling.
Stigen
Hvornår skal man forlade 2D canvas? Ikke når nogen siger det, men når man har målt det. Knappen kører Gray–Scott ved stigende gitterstørrelser på din maskine, lige nu, og tegner ms per frame. Budgettet er 16,7 ms for 60 fps.
| Gitter | Celler | ms/frame | Loft |
|---|---|---|---|
| Kør målingen. | |||
Op til nogle få tusind tegneoperationer per frame. Plade A, B og D bor her og kommer aldrig i nærheden af loftet. Fordelen er ikke ydeevne, det er at du kan læse din egen kode om et år.
Samme API, men tegningen flyttes af hovedtråden. Løser ikke at noget er langsomt; løser at det langsomme blokerer scroll og input. Det rigtige næste trin når rammerne er tunge men ikke ekstreme.
Gray–Scott hører til her. Hver celle er uafhængig af alle andre, præcis dét en GPU gør bedst. To teksturer der bytter plads, samme ligninger, hundrede gange gitteret. Prisen er at fejlsøgning bliver væsentligt sværere.
Når regnestykket ikke naturligt er "en farve per pixel", partikelfysik med kollisioner, sortering, n-legeme. Overkill til alt på denne side. Værd at kende grænsen, ikke at krydse den for tidligt.
Fælles for alle fire, de 30 linjer enhver af dem har brug for: ResizeObserver gitter og buffere følger elementets faktiske størrelse IntersectionObserver stop løkken når komponenten ikke er på skærmen visibilitychange stop når fanen går i baggrunden devicePixelRatio klippet til 2, 3× koster 2,25× fyldrate for intet prefers-reduced-motion ét stillbillede, med samme information performance.now() delta-tid, aldrig frame-tælling pointercancel ellers hænger interaktionen når systemet overtager gesten