Terminalfönster som visar en cyberpunkstad ritad helt med ASCII-tecken
Kul med teknik

En hel stad byggd av bokstäver, i en enda HTML-fil

Ett projekt som visats upp i utvecklarkretsar det senaste året renderar en gångbar cyberpunkstad helt i ASCII-tecken, direkt i webbläsaren, utan bibliotek och utan bilder. Ingen WebGL, ingen texturmapp, inga assets. Bara text som ritas om trettio gånger i sekunden i ett `

`-element. Tekniken bakom är förvånansvärt enkel att förstå och det går att bygga en fungerande miniversion på en kvällskvart.

Grundprincipen: du har ett rutnät av tecken, säg 120 kolumner och 60 rader. Varje ruta ska innehålla ett tecken och en färg. Din uppgift är att, för varje bildruta, räkna ut vilket tecken som hör hemma i vilken ruta. Sedan slänger du ut alltihop som en enda sträng.

Tvådimensionella arrayer gör hela jobbet

Det som gör ASCII-rendering begripligt är att du aldrig lämnar tvådimensionella arrayer. En vanlig 3D-motor jobbar med vertexbuffertar, shaders och ett djupbuffertsystem som körs på grafikkortet. Här gör du samma jobb i JavaScript men med tecken som pixlar och du kan läsa varje steg i en debugger. Samma sorts principer syns i en CSS-motor som renderar 3D utan WebGL, där man löser djup och projektion med helt andra verktyg men samma grundmatematik.

Uppsättningen ser ungefär likadan ut oavsett projekt. Du skapar en `Uint8Array` eller en vanlig array med `kolumner × rader` element som håller vilket tecken varje position ska ha. Ett andra fält håller färgen. I varje bildruta nollställer du båda, ritar in din geometri och konverterar sedan hela buffern till HTML som stoppas in med `innerHTML` på ett `

`-element med monospace-typsnitt.

Själva djupet skapas med samma matematik som vilken 3D-motor som helst. Du har en kamera i världskoordinater, du projicerar punkter genom att dividera med avståndet till kameran och du håller en djupbuffert så att en byggnad nära kameran skriver över en längre bort. Skillnaden mot WebGL är bara att resultatet landar i en teckenposition i stället för en pixel. En vägg som lutar bort från ljuset får ett glesare tecken, en vägg som vetter mot ljuset får ett tätare. Det är hela ljussättningen. En vanlig skala går från punkt och kolon för mörkt, via bindestreck och plustecken, upp till `#` och `@` för ljusast. Tecknens visuella täthet blir din gråskala.

Prestandan sitter nästan alltid i utskriften, inte i matematiken. Att sätta `innerHTML` en gång per bildruta med en färdigbyggd sträng går bra men att skapa tusentals ``-element med DOM-anrop går inte. Ett vanligt knep är att bara öppna ett nytt färg-span när färgen faktiskt byts, vilket i praktiken slår ihop långa horisontella sjok av samma nyans till ett enda element. En stadssiluett i mörkblått kan då gå från 7 200 span till några hundra.

Gångbarheten löses med samma logik som i vilket förstapersonsspel som helst. Du håller kamerans position och rotation i variabler, lyssnar på tangenttryckningar, uppdaterar positionen i en `requestAnimationFrame`-loop och ritar om. Kollision mot byggnader blir en kontroll mot ett tvådimensionellt rutnät över stadsplanen, ungefär tio rader kod. Interiörer och våningsplan, som brukar vara nästa steg i den här typen av projekt, kräver bara att kollisionsrutnätet får en tredje dimension.

Tolv tecken räcker för att begränsa alla val

Varför lägger folk kvällar på att rita hus med semikolon när Three.js finns installerat en `npm install` bort? Svaret handlar mindre om nostalgi än om arbetsflöde.

Med ett fast rutnät och tolv tillgängliga tecken finns det inga oändliga val att göra. Du kan inte finjustera en normalmap. Du kan inte lägga tre timmar på att välja rätt bloom-inställning. Antingen syns byggnaden eller så syns den inte. Det är samma sorts kreativa friktion som gör att fotografer ibland väljer en fast 50 mm-lins i stället för zoom.

Sedan finns det praktiska skäl. Hela projektet är en fil du kan mejla. Den öppnas i vilken webbläsare som helst, fungerar utan byggsteg, utan bundler och utan beroenden som ruttnar om arton månader. Att koda utan verktygslager är dessutom något fler utvecklare medvetet söker sig till just nu, delvis som en reaktion mot att alltid ha en AI-assistent inkopplad, ungefär samma rörelse som beskrivs i hur hobbykodare stänger av Copilot för att få tillbaka känslan.

En roterande kub är hela grunden för en stad

Börja inte med en stad. Börja med en roterande kub.

Skapa en HTML-fil med ett `

`-element, sätt `font-family: monospace` och en `line-height` som matchar teckenbredden så att rutnätet inte blir utdraget på höjden. Definiera åtta hörnpunkter i tre dimensioner. I varje bildruta roterar du dem runt Y-axeln, projicerar dem till skärmkoordinater genom att dividera x och y med z och skriver ett tecken i motsvarande position i din buffert. Rita linjerna mellan hörnen med Bresenhams algoritm, som du hittar färdig i tio rader.

När kuben snurrar har du redan allt som behövs. En stad är samma kod, fast med hundra lådor i stället för en, plus en kamera som får flyttas. Lägg till ljussättning genom att jämföra varje ytas normal mot en ljusriktning och plocka tecken ur din täthetsskala. Lägg till färg genom att spara en HEX-kod per position och gruppera dem vid utskrift. Vill du testa nyanser för neonskyltarna kan en färgkonverterare spara en del tid, eftersom HSL är betydligt lättare att jobba med än HEX när du vill ha tio varianter av samma lila.

Två fallgropar är värda att känna till i förväg. Den första är att tecken inte är kvadratiska. En typisk monospace-glyf är ungefär dubbelt så hög som bred, så om du inte kompenserar för det i projektionen blir allt platt. Den andra är att `innerHTML` med tusentals element per bildruta får även en snabb dator att haka. Mät med webbläsarens prestandaflik innan du optimerar något annat.

Det här är också en av få projekttyper där en kodassistent är genuint hjälpsam utan att ta över. Rotationsmatriser och linjeritning är väldokumenterad matematik som en modell klarar utmärkt, medan besluten om hur staden ska kännas är helt dina. Det är ungefär så AI-verktyg fungerar bäst i praktiken, som en genväg förbi det som redan är löst.

Och den färdiga filen väger under 40 kilobyte. Det är mindre än en enda skärmdump av samma stad.

Kommentera artikeln

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *