AI
Cara ChatGPT Astra Membangun Game Portofolio 3D Saya, Langkah demi Langkah
September 202612 menit baca

Versi game 3D dari portofolio ini di matthewswong.com/play. Anda tiba dengan perahu saat fajar dan menjelajahi empat pulau — Pelabuhan, Pulau Bengkel, Teluk Produk, dan Mercusuar — di mana setiap kios, bangunan, dan lampu adalah proyek, produk, atau sertifikasi nyata dari situs ini. Tujuannya menyalakan mercusuar sebelum gelap, yang memakan waktu sekitar tujuh menit.
Loop yang bisa dimainkan — rute, dunia, controller, HUD, kartu, gate, dan test — selesai dalam satu sesi pada 12 September 2026 dari PRD sepanjang 1.015 baris. Pass kedua pada 18 September membangun ulang tampilan dunia, memperbaiki bug gerakan, membuatnya berjalan di ponsel, dan menambahkan bloom. Menulis brief dan meninjau hasilnya memakan waktu saya lebih lama daripada kedua sesi itu.
Ya. Ponsel secara default mendapat tier hemat baterai dengan stik sentuh mengapung, area seret untuk melihat sekeliling, serta tombol lompat dan interaksi, dan setiap dialog dibatasi tinggi viewport sehingga bisa di-scroll di ponsel landscape. Seluruh loop diuji di Pixel 7 emulasi dengan touch event sungguhan, tetapi pengujian di perangkat fisik masih ada dalam daftar.
Brief yang bisa diverifikasinya: product requirements document dengan tujuan yang diurutkan, non-goal, budget yang ditulis sebagai gate CI, dan aturan data yang harus dipatuhi game. Setelah itu ia membutuhkan saya untuk melihat screenshot dan mengatakan apa yang terasa salah, dan ia masih membutuhkan tiga langkah yang hanya bisa dikerjakan manusia — selfie untuk avatar asli, art pass di Blender, dan pengujian di ponsel sungguhan.
Dengan mengubah setiap aturan menjadi sesuatu yang bisa dijalankan. Gate prebuild menggagalkan build jika ada proyek tanpa penempatan di dunia game; import guard menjaga three.js tetap di dalam folder game; smoke test dan soak test memori berjalan di setiap pull request; dan tiga script Playwright memutar ulang gerakan, input sentuh, dan seluruh permainan di desktop maupun ponsel. Agent juga mengambil screenshot game yang sebenarnya setelah setiap perubahan dan membaca kembali gambarnya.

Ringkasan Utama
The Archipelago adalah game portofolio 3D yang dibangun ChatGPT Astra dari brief produk sepanjang 1.015 baris: enam fase yang masing-masing punya gate, loop screenshot headless agar agent bisa melihat hasil kerjanya sendiri, dan tiga langkah yang tetap dikerjakan manusia. Tulisan ini mengurai ideasi, kerangka kerja, proses build, serta bug gerakan dan warna yang hanya terlihat oleh test.
Setiap kali membuka halaman portofolio saya sendiri, yang terlihat selalu sama: grid kartu, dua belas proyek, dan tidak ada alasan untuk mengklik satu pun. Karya di bawah lipatan layar — ERP yang menjalankan lantai pabrik, dua produk dengan pengguna nyata, enam sertifikat — sama saja disimpan di laci. September 2026 saya memutuskan portofolio ini harus menjadi tempat yang dijelajahi, bukan halaman yang di-scroll, dan saya memutuskan untuk tidak membangunnya dengan tangan.
Ini catatan bagaimana halaman itu menjadi The Archipelago, game 3D yang bisa dijelajahi di /play, dan bagaimana ChatGPT Astra membangunnya: dari brief yang saya tulis, melewati enam fase dengan gate, sampai pass kedua yang memperbaiki hal-hal yang hanya bisa dilihat test. Commit, output test, dan screenshot-nya saya simpan, jadi setiap angka di sini punya sumber.

Konsepnya lahir sebelum satu baris kode pun, dari satu aturan: sebuah game butuh satu tujuan dalam tiga puluh detik pertama. Tujuan kami: nyalakan mercusuar sebelum gelap. Anda tiba dengan perahu saat fajar, bermain sebagai saya, dan cahaya siang adalah progress bar-nya: fajar, pagi, sore, senja, satu langkah untuk setiap pulau yang selesai. Setiap pulau adalah satu bagian situs, jadi metaforanya sekaligus menentukan rekayasanya: satu pulau adalah satu chunk yang di-stream saat Anda berjalan ke arahnya.
Satu aturan desain yang saya pertahankan adalah aturan paritas: exhibit diturunkan dari modul yang sama dengan yang dirender halaman situs — lib/projects.ts, lib/certifications.ts, dan lib/products.ts — dan tidak pernah diketik ulang. Sebuah gate prebuild menggagalkan build jika ada proyek tanpa penempatan di dunia game, atau penempatan tanpa proyek. Satu aturan itulah yang membuat game tidak bisa menyimpang dari situs.
Saya tidak memberi prompt 'buatkan game portofolio 3D'. Saya minta agent riset dulu, lalu menulis product requirements document bersamanya: 1.015 baris, enam belas bagian plus lampiran. Masalah, tujuan yang diurutkan, non-goal, referensi yang benar-benar sudah dicek (versi library dari registry npm, dukungan browser dari caniuse, vendor avatar yang domainnya masih hidup), konsep, dunia, pengalaman pemain, arah seni, arsitektur, performa, keandalan, aksesibilitas, SEO, privasi, metrik, rencana pengiriman, risiko, dan pertanyaan terbuka untuk saya. PRD adalah prompt-nya; semua setelahnya adalah eksekusi.

Dua hal di brief itu bekerja lebih keras dari yang lain. Non-goal menjaga rilis pertama tetap empat pulau dengan tiga pulau lain terlihat di horizon, jadi scope tidak punya ruang untuk merayap. Dan setiap budget ditulis sebagai gate CI, bukan paragraf: import guard yang hanya mengizinkan three.js di-import di bawah folder game sendiri, sehingga sisa situs tidak bisa diam-diam menumbuhkan dependensi 3D; pemeriksaan paritas penempatan; smoke test Playwright dan soak test memori yang berjalan di setiap pull request; serta notice gate yang memberi tahu pengunjung ukuran unduhan, dihitung dari manifest build, sebelum satu byte engine pun terkirim.
Tulis brief agar mesin bisa memverifikasinya. Kalimat seperti 'game harus tetap ringan' tidak berbuat apa-apa; 'three hanya boleh di-import di bawah app/components/game dan lib/game, diperiksa saat prebuild' menjadi script yang dijalankan agent setelah setiap perubahan.
Rencana pengiriman di PRD membagi pekerjaan menjadi enam fase, masing-masing diakhiri sesuatu yang bisa diperiksa, bukan perasaan. Estimasinya mengasumsikan agent mengerjakan fase kode dan tiga langkah tetap milik manusia: selfie untuk avatar, art pass, dan melihat hasilnya.
| Fase | Hasil | Gate |
|---|---|---|
| 0 — Fondasi | Rute tanpa chrome situs, dynamic import, probe kemampuan perangkat, notice gate, fallback Peta, import guard, smoke test CI | HTML hasil build setiap halaman lama tidak berubah; smoke test hijau |
| 1 — Vertical slice | Pelabuhan, avatar pengganti, controller kapsul di atas BVH, kamera, HUD, satu kartu exhibit nyata, deep link, state tersimpan | Seluruh loop bisa dimainkan |
| 2 — Avatar | Selfie Avaturn ke GLB, klip Mixamo, pembersihan di Blender, paling besar 3 MB | Empat dari lima orang yang mengenal saya bisa menyebut nama saya dari satu still |
| 3 — Bengkel + Teluk | Seni pulau, 14 exhibit, paritas penempatan, tekstur screenshot, merge, pergantian waktu | Budget hijau; visual review pertama |
| 4 — Mercusuar | Enam lampu, pendakian, senja, sinar mercusuar, kartu finale, share | Satu permainan penuh di bawah delapan menit, tanpa jalan buntu |
| 5 — Peluncuran | Kontrol sentuh, aksesibilitas, salinan bahasa Indonesia, still OG, SEO, matriks perangkat, analytics, dokumentasi | Setiap baris performa dan keandalan hijau |
Fase 0 dan 1, plus sisi kode dari fase 3 dan 4, selesai dalam satu sesi pada 12 September 2026 dengan dunia prosedural dan manekin sebagai pengganti avatar. Dua penyimpangan dari brief layak disebut, karena keduanya adalah agent yang membantah dengan bukti: ia menyingkirkan header dan footer dari rute game lewat pengecekan pathname, bukan pemisahan route group yang diminta PRD, karena 32 script dan dokumen menuliskan folder pages secara hardcode; dan ia mengunci React di 19.2.7 karena React Three Fiber 9.7 mendeklarasikan rentang versi yang tidak mencakup 19.3.
Yang tidak bisa diukur gate adalah hal pertama yang saya lihat pada 18 September: game-nya benar dan jelek. Pulau seperti chip poker, pohon kelapa lima kerucut di atas tongkat, setiap bangunan kotak dengan atap lempengan, dan saya adalah kapsul hitam. Maka pass kedua mendapat brief-nya sendiri, dan agent membangun ulang dunia tanpa mengunduh satu model pun: tanah pulau sebagai satu geometri organik yang dibaca renderer sekaligus collider, sehingga pantai yang Anda lihat adalah pantai yang Anda tabrak; pohon kelapa dengan batang melengkung berbuku dan pelepah yang menjuntai; rumput, semak, dan batu yang disebar generator berseed yang menghindari setiap jejak bangunan dan jalur; tekstur yang digambar di canvas saat runtime — papan kayu, atap rumbia, seng bergelombang; air yang tahu di mana setiap pantai berada, dengan perairan dangkal dan garis buih; serta tujuh bangunan yang dibangun ulang menjadi warung beratap rumbia, kios beratap limas, gudang ERP berdinding seng, gym berkaca, bay cuci mobil, tiang lampu, dan mercusuar bergaris.

Instancing adalah alasan biayanya tetap masuk akal: setiap benda statis di satu pulau — ratusan rumpun rumput, setiap pelepah, setiap batu — adalah satu instanced draw yang matriksnya ditulis sekali saat mount, bukan dekomposisi ulang per frame yang dilakukan helper library secara default. Soak test memori setelah pass ini memutar pulau tiga kali: heap 21,2 MB pada baseline, 21,9 MB setelah siklus, 19,1 MB setelah keluar dan kembali, dengan jumlah geometri dan tekstur kembali ke baseline. Test itu ada karena brief menuntutnya; kali ini ia tidak menangkap apa pun, dan justru itulah intinya.
Setiap perubahan di pass kedua melewati loop yang sama. Agent membaca file yang akan diubah dan semua yang meng-import-nya, merencanakan, mengimplementasikan, menjalankan typechecker, lalu melakukan langkah yang membuat perbedaan: merender game yang sebenarnya secara headless dan melihat gambarnya. Chromium dalam mode headless bisa menjalankan WebGL2 lewat SwiftShader tanpa GPU, dan game ini sudah mengekspos debug bridge kecil di development untuk soak test-nya — teleport, pergi ke satu pulau, membaca counter renderer. Dua puluh baris Playwright mengubahnya menjadi mata.
// scratch/shoot.mjs — the agent's eyes. Headless Chromium renders WebGL2
// through SwiftShader, so no GPU is needed; the debug bridge the game exposes
// in development (window.__archipelago) moves the player without walking.
const browser = await chromium.launch({
headless: true,
args: ["--use-gl=angle", "--use-angle=swiftshader", "--enable-unsafe-swiftshader"],
});
const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
await page.goto("http://localhost:3000/en/play/", { waitUntil: "networkidle" });
await page.getByRole("button", { name: /set sail/i }).click();
await page.locator("canvas").first().waitFor({ state: "visible" });
for (const island of ["workshop", "cove", "lighthouse"]) {
await page.evaluate((id) => window.__archipelago.goto(id), island);
// The follow camera is critically damped: give it two seconds to settle,
// or every still is a smear of the previous position.
await page.waitForTimeout(2000);
await page.screenshot({ path: "shots/" + island + ".png" });
}
// stats() returns draw calls, triangles, geometry and texture counts —
// the numbers the soak test watches, read back after every change.
console.log(await page.evaluate(() => window.__archipelago.stats()));Di bawah software renderer, game berjalan sekitar tiga frame per detik, jadi script menunggu kamera yang teredam berhenti bergoyang sebelum setiap still, dan agent membaca kembali setiap gambar sebelum memutuskan perubahan berikutnya. Begitulah ia menyadari pita pucat di horizon (warna fog tidak cocok dengan langit), rumpun rumput yang terlihat seperti paku, dan buih yang menggumpal di perairan dangkal — tidak satu pun akan dilaporkan oleh gate mana pun.

Gerakannya terasa salah dan saya tidak bisa menjelaskan mengapa, maka alih-alih mendeskripsikannya saya meminta reproduksi. Agent menulis script Playwright yang menahan tombol selama enam detik dan mencuplik posisi pemain serta yaw kamera lewat bridge. Run baseline-nya memberi angka pada tiga perasaan sekaligus.
Berjalan mundur memutar kamera 7,4 radian dalam enam detik dan pemain tidak pernah kembali, karena kamera otomatis menyelaraskan diri di belakang avatar saat mundur: tekan mundur, avatar berbalik menghadap kamera, kamera berayun, arah depan terbalik, dan Anda berjalan melingkar. Jalan lurus ke Pulau Bengkel macet di tepi pelabuhan selama 55 detik karena akselerasi di-clamp per sumbu, sehingga arah melenceng delapan belas derajat di setengah detik pertama dan jalurnya melengkung keluar dari jalan pasir ke air. Dan bibir pulau serta tepi jalan pasir berperilaku seperti tembok, karena kapsul tabrakan dimulai dari kaki.
// Wrong: each axis is clamped on its own, so a diagonal target ramps the
// shorter axis to full speed first. For half a second the velocity points
// along the diagonal, the avatar turns to face it, the camera aligns behind
// the avatar, and "forward" has quietly rotated. Over 50 m the path curves
// off a 4 m causeway onto the beach, where the water blocks both axes.
velocity.x += clamp(wanted.x - velocity.x, -maxChange, maxChange);
velocity.z += clamp(wanted.z - velocity.z, -maxChange, maxChange);
// Right: clamp the change *vector*, so the velocity ramps along the wanted
// direction from the first step and the heading never drifts.
let dx = wanted.x - velocity.x;
let dz = wanted.z - velocity.z;
const change = Math.hypot(dx, dz);
if (change > maxChange) {
dx *= maxChange / change;
dz *= maxChange / change;
}
velocity.x += dx;
velocity.z += dz;Perbaikannya kecil setelah angkanya ada: selaraskan kamera hanya saat input mendorong ke depan, clamp vektor perubahannya, mulai kapsul tabrakan 0,4 meter di atas kaki sehingga apa pun yang lebih rendah adalah anak tangga yang dinaiki oleh ground snap, dan lebarkan jalan pasir dari empat meter menjadi lima. Sore yang sama, run di ponsel menemukan dua bug lagi yang tidak akan ditemukan test desktop mana pun: panel logbook duduk tepat di tempat jempol kiri mendarat dan menelan setiap sentuhan, sehingga stik tidak pernah muncul; dan menu jeda lebih tinggi dari ponsel landscape dan tidak bisa di-scroll, sehingga tombol Resume berada di luar layar tanpa cara menjangkaunya. Tiga script kini berjaga:

Langkah grafis terakhir adalah bloom di tier tinggi agar lampu, lentera, dan matahari bersinar. Alih-alih hanya memoles, ia membuka bug sungguhan: dengan composer post-processing di dalam rantai, setiap still kembali pucat dan pudar. Ambang bloom adalah tersangka pertama, dan tersangka yang salah. Shader langit dan air tidak pernah menyertakan transformasi output renderer, sehingga tanpa composer keduanya tampil terlalu gelap dan jenuh — dan palet warnanya disetel terhadap tampilan itu — sementara lewat composer keduanya dikonversi dengan benar dan terlihat pucat. Dua tier menampilkan dua game yang berbeda. Perbaikannya: rutekan kedua shader lewat tone mapping dan konversi ruang warna yang sama dengan setiap material lain, lalu nyatakan ulang paletnya sekali; tier hemat baterai dan tier desktop kini merender gambar yang sama.
Tiga hal tetap milik manusia, persis seperti kata brief. Agent tidak bisa mengambil selfie yang dibutuhkan avatar asli, sehingga pengelana berransel itu adalah pengganti. Ia tidak bisa mengerjakan art pass di Blender yang akan menggantikan pulau prosedural dengan pulau buatan tangan. Dan ia tidak bisa berdiri di ruangan dengan ponsel sungguhan; setiap hasil mobile di sini berasal dari Pixel 7 emulasi. Di luar itu, satu pelajaran dari proyek sebelumnya tetap berlaku: gate memverifikasi budget dan tidak bisa melihat kebosanan, jadi visual review adalah langkah terjadwal dengan tanggal, bukan sesuatu yang saya harapkan terjadi sendiri.

Game-nya sudah tayang di matthewswong.com/play, dalam bahasa Inggris dan Indonesia, di desktop, layar sentuh, dan gamepad. Setiap exhibit di dalamnya adalah proyek, produk, atau sertifikat nyata dari situs ini, dan kotak surat di pelabuhan adalah halaman kontak.
Aturan yang saya bawa pulang: tulis brief agar mesin bisa memverifikasinya, buat mesin melihat hasil kerjanya sendiri, dan biarkan tiga langkah yang hanya bisa dikerjakan manusia tetap dikerjakan manusia. Agent yang diberi satu paragraf akan membangun satu paragraf. Diberi PRD dengan gate, loop screenshot, dan ruang untuk membantah, ia membangun dunia yang tidak akan saya selesaikan sendirian — dan memberi tahu saya, dengan angka, di mana ia keliru.
Sumber dan bacaan lanjutan