- Eksperimen yang merender 3D DOOM hanya dengan CSS, dengan semua dinding dan objek dibangun dari
<div> serta transformasi 3D (transform)
- Logika game ditangani oleh JavaScript, tetapi rendering sepenuhnya dilakukan oleh CSS, untuk mengeksplorasi batas browser dan CSS modern
- Memanfaatkan fitur CSS modern seperti trigonometri, clip-path, @property, filter SVG, dan anchor positioning untuk membuat dinding, lantai, pencahayaan, sprite, hingga efek ledakan
- Karena CSS tidak memiliki konsep kamera, sudut pandang ditangani dengan menggerakkan dunia alih-alih pemain, dan semua pergerakan dikendalikan lewat pembaruan properti kustom
- Performanya memang belum setara WebGL, tetapi ini menunjukkan kemungkinan perluasan kemampuan ekspresi dan komputasi CSS
Rendering 3D DOOM dengan CSS
- Proyek eksperimen yang merender DOOM hanya dengan CSS, dengan semua dinding, lantai, dan objek disusun dari
<div> dan ditempatkan menggunakan transformasi 3D (transform)
- Logika game berjalan di JavaScript, tetapi rendering sepenuhnya ditangani oleh CSS
- Tujuan proyek ini adalah mengeksplorasi batas browser dan CSS modern
Kembali ke matematika SMA
- Mengekstrak data file WAD DOOM asli (vertices, linedefs, sidedefs, sectors) untuk membangun adegan statis dari ribuan
<div>
- Setiap dinding menerima koordinat awal-akhir serta tinggi lantai-langit-langit melalui properti kustom CSS
- Fungsi CSS
hypot() dan atan2() digunakan untuk menghitung panjang dinding dan sudut rotasinya
- JavaScript mengirim data mentah, lalu CSS menghitung trigonometri untuk melakukan rendering
- Game loop dan renderer dipisahkan, sehingga JS hanya menangani manajemen status dan pembaruan koordinat
Masalah transformasi sistem koordinat
- DOOM memakai sistem koordinat 2D dengan Y bertambah ke utara, sementara CSS 3D memakai Y ke atas dan Z mengarah ke penonton
- Saat transformasi, digunakan bentuk
translate3d(x,-z,-y) untuk menyelaraskan sistem koordinat
- Yang menarik, perhitungan
rotateY(atan2(var(--delta-y), var(--delta-x))) berjalan tanpa transformasi tambahan
Menggerakkan dunia alih-alih kamera
- CSS tidak memiliki konsep kamera, jadi dipakai pendekatan menggerakkan dunia secara terbalik terhadap pemain
- Hanya empat properti kustom
--player-x/y/z/angle yang diperbarui dari JS
translate: 0 0 var(--perspective) dipakai untuk koreksi sudut pandang, sementara rotateY dan translate3d menangani rotasi pandangan dan perpindahan posisi
- Semua pergerakan ditangani hanya lewat pembaruan properti
Lantai adalah div yang direbahkan
- Karena elemen DOM dasar berupa bidang vertikal, lantai ditempatkan horizontal dengan
rotateX(90deg)
clip-path, polygon(), dan path() digunakan untuk merepresentasikan area poligon kompleks dan lubang
- Fungsi CSS modern
shape() memungkinkan penggunaan jalur berbasis persentase bersama aturan evenodd
Penyelarasan tekstur
- Agar tekstur antarsektor yang bersebelahan tidak terputus, digunakan
background-position berbasis koordinat dunia
- Semua sektor berbagi grid tekstur yang sama untuk menghasilkan sambungan batas yang mulus
Pintu, lift, dan animasi @property
- Pembukaan pintu diwujudkan dengan menaikkan langit-langit sektor, dan
transform pada kontainer <div> ditangani lewat transition CSS
- Lift menggerakkan pemain bersama platform, sehingga JS menyinkronkan
--player-z
- Dengan
@property, properti kustom didaftarkan sebagai nilai numerik agar efek jatuh dan perpindahan bisa halus
Sprite dan mirroring
- Sprite musuh selalu menghadap kamera dengan pendekatan billboard
- Dari 8 arah, hanya 5 set yang memakai gambar asli, sisanya ditangani dengan pembalikan kiri-kanan (
scaleX)
- Pergantian frame berjalan, menyerang, dan mati menggunakan animasi
steps()
- Masalah semua musuh berjalan serempak diatasi dengan
animation-delay acak dari JS
Proyektil, ledakan, dan efek peluru
- Roket, bola api, dan sejenisnya dipindahkan otomatis dari A→B lewat animasi CSS
- JS hanya menetapkan koordinat awal-akhir dan durasi, lalu saat tabrakan elemen dihapus dan sprite ledakan dibuat
- Ledakan dan asap peluru dihapus otomatis setelah animasi 3 frame berbasis
steps()
Pencahayaan dan filter
- Nilai kecerahan per sektor ditetapkan lewat properti
--light, lalu elemen di dalamnya mewarisi filter: brightness()
- Lampu berkedip dibuat dengan
@keyframes yang mengubah nilai --light secara berkala
- Musuh transparan (Spectre) direpresentasikan sebagai siluet terdistorsi dengan filter SVG (
feColorMatrix, feTurbulence, feDisplacementMap)
UI responsif dan anchor positioning
- Game ini responsif untuk mobile, dan HUD membungkus baris dengan
flex-wrap
- Sprite senjata menyesuaikan posisi otomatis terhadap tinggi HUD menggunakan
anchor-name / position-anchor
- Tombol kontrol sentuh juga ditempatkan dengan pendekatan anchor yang sama
Mode penonton
- Mendukung tampilan seluruh peta dan sudut pandang kejar orang ketiga
- Fungsi CSS
sin() dan cos() digunakan untuk menghitung posisi kamera di belakang pemain
- Properti
rotate dan translate dipisahkan untuk menghasilkan transisi sudut pandang yang halus
- JS hanya memperbarui posisi dan sudut, sedangkan matematika kamera ditangani oleh CSS
Culling dan performa
- Ribuan elemen 3D menimbulkan beban pada compositor browser
- Culling berbasis JS: elemen di luar pandangan diatur menjadi
hidden
- Eksperimen culling berbasis CSS: mengontrol
visibility lewat nilai terhitung, dengan trik type grinding
- Jika fungsi
if() distandardisasi, ini bisa diganti dengan ekspresi kondisi yang lebih ringkas
Pengurutan kedalaman
- Browser menangani pengurutan kedalaman (z-order) secara otomatis
- Objek pada bidang yang sama diberi offset kecil untuk mencegah flicker
“Tipuan” DOOM dan penanganan langit
- DOOM asli memakai trik proyeksi dengan menggambar langit sebagai tekstur 2D “di atas” dinding
- Karena renderer CSS harus menempatkan langit di ruang 3D nyata, pada beberapa adegan muncul masalah bagian belakang peta terlihat
- Solusinya adalah mengecualikan elemen di belakang dinding langit dari rendering pada tahap culling
Kesimpulan — batas dan kemungkinan CSS
- Seluruh game loop dipisahkan: JS untuk logika, sementara rendering berbasis CSS murni
- Fitur CSS modern seperti trigonometri, @property, clip-path, filter SVG, dan anchor positioning didorong sampai batas maksimal
- Walau belum setara performa WebGL, proyek ini membuktikan potensi perluasan daya ekspresi CSS
- Ditemukan banyak bug 3D dan isu performa di Safari dan Chrome
- Kesimpulan akhirnya: “Bisakah DOOM dijalankan dengan CSS?”
→ Bisa. Yes, it can.
1 komentar
Komentar Hacker News
Menurut saya, orang-orang tipe “saya menjalankan ini dengan DOOM” seharusnya direkrut oleh divisi sistem propulsi luar angkasa pemerintah
Mereka adalah tipe orang yang butuh tantangan yang sangat tidak biasa, bukan sekadar sibuk menggerakkan jari
Ini terasa seperti proyek tipe “karena bisa, jadi dilakukan”
CSS awalnya adalah bahasa styling deklaratif, tetapi sekarang terus berubah menjadi sistem yang dapat diprogram dengan hadirnya kondisi, fungsi matematika, dan trik rendering
Yang penting bukan “apakah DOOM bisa dijalankan dengan CSS”, melainkan seberapa banyak logika yang kita dorong ke layer yang sebenarnya tidak dirancang untuk itu
CSS menyembunyikan hasratnya untuk menjadi bahasa pemrograman, tetapi pada akhirnya malah berubah menjadi abstraksi yang sepenuhnya keliru
Dulu kita perlu JS untuk dropdown, tooltip, dan layout, tetapi sekarang properti CSS sudah bisa menentukan anchor positioning bahkan kondisi
if()Animasi, toggle detail, sampai efek terkait aksesibilitas sekarang juga bisa ditangani dengan CSS
Membuat adegan 3D dengan CSS sebenarnya sudah lama memungkinkan, tetapi interaksi tetap memerlukan JS
Sekarang, seperti pada proyek x86CSS, bahkan CPU bisa diemulasikan hanya dengan CSS tanpa JS
Karena itu, menarik untuk bertanya apakah DOOM juga bisa diwujudkan secara real-time dengan CSS murni
Kasus ini menunjukkan dengan sangat jelas mengapa orang menginginkan CSS berbasis TypeScript
Karena fitur seperti
if()yang hanya bekerja di Chrome, para developer akhirnya memakai trik-trik seperti iniMisalnya, menggunakan
animation-delaydan@keyframesuntuk meniru toggle visibilitasJika CSS
if()distandardisasi, kondisi bisa ditangani dengan rapi tanpa perlu hack seperti ituCheat code DOOM IDDQD dan IDKFA sayangnya tidak berfungsi
Ini mengingatkan pada masa ketika membuat sudut membulat pada div memerlukan empat GIF
Benar-benar mengesankan! Cukup hapus satu div saja dan kita bisa melakukan wall hack
.wallnilaiopacity: 0.7dan nuansa transparent wall hack gaya lama bisa direproduksi persisSaya sempat berpikir, “di mana saya bisa mencobanya langsung?” dan ternyata bisa di cssdoom.wtf
Di Chromium justru terasa lebih tersendat, dan saya tidak menemukan tombol strafing
Meski begitu, secara keseluruhan ini implementasi yang luar biasa
CSS adalah spesifikasi representatif yang menunjukkan batas desain oleh komite
Bersama SVG, ia bersaing untuk gelar “spesifikasi paling jelek dilihat”
Satu tambahan tentang implementasi keren ini:
sebenarnya bukan pemain yang bergerak, melainkan dunianya yang bergerak
Kamera hanyalah alat konseptual untuk menghitung sudut pandang (frustum)