<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vampire.js</title>
    <description>The latest articles on DEV Community by Vampire.js (@vampirejs).</description>
    <link>https://dev.to/vampirejs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2389797%2Fb2a54ba3-aef4-42c4-9dd3-716e4340f23f.png</url>
      <title>DEV Community: Vampire.js</title>
      <link>https://dev.to/vampirejs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vampirejs"/>
    <language>en</language>
    <item>
      <title>Building a 3D Exploration Game with Three.js</title>
      <dc:creator>Vampire.js</dc:creator>
      <pubDate>Sun, 27 Sep 2026 18:03:39 +0000</pubDate>
      <link>https://dev.to/vampirejs/building-a-3d-exploration-game-with-threejs-2198</link>
      <guid>https://dev.to/vampirejs/building-a-3d-exploration-game-with-threejs-2198</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8dnb86johxolhtro7qkt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8dnb86johxolhtro7qkt.png" alt="Banner" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Back story (For performance optimizations, skip to the next section ;))
&lt;/h2&gt;

&lt;p&gt;The game development club I am a part of had to present themselves in front of the freshers at my college, and being a Technical Head at the club, I thought it would be cool if we did something that sets us apart from the other clubs in the first impression itself.&lt;br&gt;
Hence, we decided to make a game, and let the crowd play it during the presentation.&lt;/p&gt;

&lt;p&gt;So, the requirements were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A good game that looks polished and feels good&lt;/li&gt;
&lt;li&gt;Performs good on mobile devices&lt;/li&gt;
&lt;li&gt;Is fun to play&lt;/li&gt;
&lt;li&gt;Should be easy to distribute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To take care of the 4th requirement, we decided to make it web based, so that just a QR code would be enough to distribute it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Concept
&lt;/h3&gt;

&lt;p&gt;I've been a fan of Bruno Simon's work since years, and especially after he recently made a new portfolio which is even better than his old one, I too wanted to try making something like that, as I am really fascinated by technologies like three.js, computer graphics and shaders.&lt;/p&gt;

&lt;p&gt;So we decided to do a similar concept, with a car to navigate around an isolated island where we have a mini-game, and zones to display information in.&lt;/p&gt;

&lt;p&gt;Now comes the real challenge, how to use shaders and other fancy elements, but at the same time, also make sure it performs good on mobile devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tech Stack Used
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Three.js&lt;/strong&gt; (WebGL 3D rendering)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript&lt;/strong&gt; (Typed application logic)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cannon.es&lt;/strong&gt; (Rigid-body physics engine)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GLSL&lt;/strong&gt; (Custom shaders for environment effects)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Process
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Phase 1: Baselines &amp;amp; Primitives&lt;/strong&gt;
We started by assembling a basic scene using primitive boxes, spheres, and plane colliders to establish a sandbox baseline for driving controls and camera behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvc8cnjyom0whv3j5s4k3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvc8cnjyom0whv3j5s4k3.png" alt="Baseline Sandbox Primitives" width="799" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Phase 2: Scenery &amp;amp; Models&lt;/strong&gt;
Once driving and target follow felt right, we designed and integrated basic meshes for the car, trees, and obstacles to outline our world.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0l5o47br6i8g6tjb4xar.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0l5o47br6i8g6tjb4xar.png" alt="Scenery and Basic Meshes" width="800" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Phase 3: Visual Polish &amp;amp; Mobile Optimization&lt;/strong&gt;
As we refined the art, we faced two overlapping challenges:

&lt;ol&gt;
&lt;li&gt;We needed beautiful visual elements (soft grass, atmospheric shadows, ambient wildlife).&lt;/li&gt;
&lt;li&gt;The entire interactive sandbox had to run smoothly on mobile browsers.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To balance visual density with high performance, we implemented custom lightweight shaders and instanced geometry systems directly in GLSL to minimize draw calls and vertex overhead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhzpifjw4zq8grqvk156q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhzpifjw4zq8grqvk156q.png" alt="Shader and Instanced Effects" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This approach kept the engine pipeline extremely slim, leaving plenty of performance budget to support dynamic scenery and wildlife:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Waving grass:&lt;/strong&gt; 15,000 instanced blade planes, animated on the GPU and smoothly faded toward the boundary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Procedural flowers:&lt;/strong&gt; Sparser colorful accents scattered using non-overlapping positions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flapping butterflies:&lt;/strong&gt; Low-poly instanced wing meshes with sinusoidal fluttering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leaping dolphins:&lt;/strong&gt; Low-poly models following deep-water circular paths outside the flat island.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By shifting all repeating visual layout and animation onto the GPU, the CPU only handles simple timeline updates.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fill-Rate &amp;amp; Composition Optimizations
&lt;/h4&gt;

&lt;p&gt;Web browsers on mobile devices are heavily bottlenecked by fill rate and complex composition passes. Common post-processing pipelines (such as SSAO or Bloom) are extremely expensive to run on mobile. We bypassed these entirely with cheap visual compromises:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pseudo-AO:&lt;/strong&gt; Instead of computing screen-space ambient occlusion, we shaded the base of our grass blades darker. Since grass blankets most of the ground, this simple trick convincingly simulates shadows in cracks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSS Color Filters:&lt;/strong&gt; To avoid rendering filmic LUTs dynamically via WebGL shaders, we composited a static CSS gradient over our canvas and used basic CSS hardware-accelerated filters to color grade:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="nt"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;sepia&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;25&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;hue-rotate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-8deg&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;saturate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;contrast&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Map Design
&lt;/h3&gt;

&lt;p&gt;Since Three.js does not provide a level editor out-of-the-box, we built a custom level design system powered by a grayscale density mask. This significantly simplified map restructuring and content changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Paths, spawns, and clearings are painted black to exclude foliage.&lt;/li&gt;
&lt;li&gt;Meadows are painted white to distribute instanced grass.&lt;/li&gt;
&lt;li&gt;Adding a new island or path at runtime is as simple as painting a new shape on the mask.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F60fgtuigkp2164jfq1v0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F60fgtuigkp2164jfq1v0.png" alt="Grass Mask Tiling Setup" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Through these performance compromises, the complete, structured islands maintain high frame rates on both mobile and desktop platforms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2xabpqtvw4jb3etz786f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2xabpqtvw4jb3etz786f.png" alt="Final Exploration Sandbox" width="800" height="388"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Final look of the sunset exploration island&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;The final game: &lt;a href="https://gdu-sip.netlify.app/" rel="noopener noreferrer"&gt;https://gdu-sip.netlify.app/&lt;/a&gt;&lt;br&gt;
Bruno simon's portfolio: &lt;a href="https://bruno-simon.com/" rel="noopener noreferrer"&gt;https://bruno-simon.com/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
