<?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: no momo</title>
    <description>The latest articles on DEV Community by no momo (@no_momo).</description>
    <link>https://dev.to/no_momo</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%2F3005873%2Ff009505a-a9e6-4561-b197-a6499de75060.png</url>
      <title>DEV Community: no momo</title>
      <link>https://dev.to/no_momo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/no_momo"/>
    <language>en</language>
    <item>
      <title>When I Used AI to Write an HTML Game but Was Forced to Learn WebGPU Compute Shaders</title>
      <dc:creator>no momo</dc:creator>
      <pubDate>Mon, 20 Jul 2026 07:47:58 +0000</pubDate>
      <link>https://dev.to/no_momo/when-i-used-ai-to-write-an-html-game-but-was-forced-to-learn-webgpu-compute-shaders-1g0f</link>
      <guid>https://dev.to/no_momo/when-i-used-ai-to-write-an-html-game-but-was-forced-to-learn-webgpu-compute-shaders-1g0f</guid>
      <description>&lt;p&gt;Here’s the thing.&lt;/p&gt;

&lt;p&gt;Last week I wanted to build a simple particle firework effect – the kind where you click the screen and hundreds of colorful particles explode, fall, and fade away. I’d written something similar with Canvas 2D before, so I knew the drill. But this time I decided to be lazy: I dumped the requirements into Claude and asked it to generate a complete HTML file on the spot.&lt;/p&gt;

&lt;p&gt;The AI gave me working code in maybe fifteen seconds. Clean, well‑commented, even with gradient‑colored particles. I opened it in the browser, clicked a couple of times – &lt;strong&gt;200 particles&lt;/strong&gt;, &lt;strong&gt;smooth as butter&lt;/strong&gt;. Then I bumped the count to &lt;strong&gt;2,000&lt;/strong&gt; – the frame rate tanked to &lt;strong&gt;~35 fps&lt;/strong&gt;. At &lt;strong&gt;5,000 particles&lt;/strong&gt;, it became a slideshow at &lt;strong&gt;12 fps&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That’s when it hit me: AI can write code that runs, but it won’t help you when it runs like crap.&lt;/p&gt;

&lt;p&gt;So I went down a frustrating but incredibly rewarding debugging rabbit hole. And the thing that finally saved me wasn’t a smarter AI – it was &lt;strong&gt;WebGPU’s compute shaders&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;Step 1: Finding the Real Bottleneck&lt;/p&gt;

&lt;p&gt;The AI‑generated code looked like this (simplified):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;particles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;particles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;vx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;vy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;life&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`hsl(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, 100%, 60%)`&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;particles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vy&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// gravity&lt;/span&gt;
    &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;life&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clearRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;particles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;globalAlpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;life&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;life&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The logic is straightforward: &lt;strong&gt;update&lt;/strong&gt; loops over 5,000 particles on the CPU, recalculating positions and velocities; &lt;strong&gt;draw&lt;/strong&gt; loops over them again, calling Canvas 2D APIs to draw circles.&lt;/p&gt;

&lt;p&gt;So where’s the problem? &lt;strong&gt;5,000 calls to&lt;/strong&gt; &lt;strong&gt;beginPath + arc + fill&lt;/strong&gt; is simply too heavy for Canvas 2D. Chrome’s Performance panel showed that the draw function consumed 78% of the frame time, most of it inside fill itself.&lt;/p&gt;

&lt;p&gt;I tried all the usual optimizations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Batch drawing? Not possible – every particle has its own color and alpha.&lt;/li&gt;
&lt;li&gt;Use ImageData and manipulate pixels directly? Too much hassle, and I’d lose anti‑aliasing.&lt;/li&gt;
&lt;li&gt;Reduce particle count? Then the firework wouldn’t look impressive enough.
I needed a way to &lt;strong&gt;offload both the computation and the heavy drawing from the CPU to the GPU&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Step 2: What the Heck Is a WebGPU Compute Shader? (In Human Language)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;WebGPU has two main use cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Rendering (Render Pass)&lt;/strong&gt; – drawing triangles, textures, etc. – the WebGL replacement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Computation (Compute Pass)&lt;/strong&gt; – running massive parallel math on the GPU and reading the results back.
A compute shader is basically a “&lt;strong&gt;mini‑program that runs on your graphics card&lt;/strong&gt;.” It doesn’t care about drawing – it only crunches numbers. And its superpower is that it &lt;strong&gt;can perform the exact same operation on thousands of data elements simultaneously, with each element completely independent&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My 5,000 particles all follow the exact same update logic: &lt;em&gt;position += velocity, velocity += gravity, life -= 0.01&lt;/em&gt;. That’s a perfect fit for parallelism.&lt;/p&gt;

&lt;p&gt;So I rewrote the &lt;em&gt;update&lt;/em&gt; function as a WebGPU compute shader, &lt;strong&gt;submitting it once per frame to let the GPU calculate all 5,000 new particle states in one go&lt;/strong&gt;, then either reading the results back to the CPU (or directly piping them into the render pipeline).&lt;/p&gt;

&lt;p&gt;Here’s the core part.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 3: A Minimal Working WebGPU Compute Shader&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, I defined a particle data structure (in WGSL, WebGPU’s shading language):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="mf"&gt;1.&lt;/span&gt;&lt;span class="c1"&gt;// Each particle occupies 16 bytes (16‑byte aligned)&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="py"&gt;.struct&lt;/span&gt; &lt;span class="n"&gt;Particle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mf"&gt;3.&lt;/span&gt;    &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;vec2&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;f32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mf"&gt;4.&lt;/span&gt;    &lt;span class="n"&gt;vel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;vec2&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;f32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mf"&gt;5.&lt;/span&gt;    &lt;span class="n"&gt;life&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mf"&gt;6.&lt;/span&gt;    &lt;span class="n"&gt;pad&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// alignment padding&lt;/span&gt;
&lt;span class="mf"&gt;7.&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the compute shader entry point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="mf"&gt;1.&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;read_write&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;particles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Particle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="mf"&gt;2.&lt;/span&gt;
&lt;span class="mf"&gt;3.&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;compute&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;workgroup_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="py"&gt;.fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;builtin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;global_invocation_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mf"&gt;5.&lt;/span&gt;    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="py"&gt;.x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="mf"&gt;6.&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nf"&gt;arrayLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;particles&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="mf"&gt;7.&lt;/span&gt;
&lt;span class="mf"&gt;8.&lt;/span&gt;    &lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;particles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="mf"&gt;9.&lt;/span&gt;
&lt;span class="mf"&gt;10.&lt;/span&gt;    &lt;span class="c1"&gt;// Physics update (identical to the JS version)&lt;/span&gt;
&lt;span class="mf"&gt;11.&lt;/span&gt;    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="py"&gt;.pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="py"&gt;.pos&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="py"&gt;.vel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="mf"&gt;12.&lt;/span&gt;    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="py"&gt;.vel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="py"&gt;.vel&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;vec2&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;f32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="mf"&gt;13.&lt;/span&gt;    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="py"&gt;.life&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="py"&gt;.life&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="mf"&gt;14.&lt;/span&gt;
&lt;span class="mf"&gt;15.&lt;/span&gt;    &lt;span class="c1"&gt;// Bounce off walls (simple)&lt;/span&gt;
&lt;span class="mf"&gt;16.&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="py"&gt;.pos.x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="py"&gt;.pos.x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="py"&gt;.vel.x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="py"&gt;.vel.x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="mf"&gt;17.&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="py"&gt;.pos.y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="py"&gt;.pos.y&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="py"&gt;.vel.y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="py"&gt;.vel.y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="mf"&gt;18.&lt;/span&gt;
&lt;span class="mf"&gt;19.&lt;/span&gt;    &lt;span class="n"&gt;particles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="mf"&gt;20.&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shader gets compiled, and each frame I submit it to the GPU – &lt;strong&gt;one submission updates all particles&lt;/strong&gt;. &lt;em&gt;@workgroup_size(64)&lt;/em&gt; means each workgroup processes 64 particles; the GPU automatically schedules a huge number of threads in parallel.&lt;/p&gt;

&lt;p&gt;In JavaScript, the invocation looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;computePass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beginComputePass&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;computePass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setPipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;computePipeline&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;computePass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBindGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bindGroup&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;computePass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchWorkgroups&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;particleCount&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;computePass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s it. The GPU does the heavy lifting in the background, and the CPU no longer has to loop over 5,000 items.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 4: The Real Pain Wasn’t Writing the Code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At this point you might think everything went smoothly – but the hardest part of the whole process wasn’t writing the WGSL. It was debugging.&lt;/p&gt;

&lt;p&gt;WebGPU compute shaders have almost no debugging tools. You can’t &lt;em&gt;console.log&lt;/em&gt; a particle’s position, and you can’t set breakpoints inside the shader. You’re left with:&lt;/p&gt;

&lt;p&gt;Reading the result buffer back to the CPU and logging it with JavaScript – which kills performance.&lt;br&gt;
Guessing by visual output – for example, if particles fly off‑screen, you suspect coordinate normalization issues or buffer alignment mistakes.&lt;br&gt;
I spent a solid two hours stuck on one stupid problem: &lt;strong&gt;WGSL’s &lt;em&gt;vec2&lt;/em&gt; has 8‑byte alignment, and JavaScript’s &lt;em&gt;Float32Array&lt;/em&gt; stores data sequentially – but I forgot to set the correct stride when creating the buffer&lt;/strong&gt;. The particles’ positions were completely scrambled, as if they’d been randomly thrown outside the canvas.&lt;/p&gt;

&lt;p&gt;The fix came from a brutally primitive approach: I wrote a small utility to copy the GPU buffer data back to the CPU, printed the first 10 particle positions, compared them with the JavaScript version, and that’s when I spotted the misalignment.&lt;/p&gt;

&lt;p&gt;This experience taught me a hard lesson: &lt;strong&gt;WebGPU is powerful, but it’s incredibly unforgiving for beginners&lt;/strong&gt;. AI won’t help you debug, and search engines won’t find your exact error – you just have to grit your teeth and read the spec.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 5: The Final Result (No Made‑Up Numbers, Just My Honest Impression)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On my MacBook Pro (M1 Pro):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;5,000 particles with Canvas 2D&lt;/strong&gt; – about &lt;strong&gt;12–15 fps&lt;/strong&gt;, and the page felt sluggish.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After switching to WebGPU compute shaders + only passing the final positions back to Canvas 2D for drawing&lt;/strong&gt; – rock‑solid &lt;strong&gt;60 fps&lt;/strong&gt;, and CPU usage dropped from &lt;strong&gt;~70%&lt;/strong&gt; to &lt;strong&gt;~8%&lt;/strong&gt;.
Note that I didn’t move the drawing to the GPU (that would require a full render pipeline) – I only moved the computation to the GPU and kept using Canvas 2D’s &lt;em&gt;fillRect&lt;/em&gt; or &lt;em&gt;arc&lt;/em&gt; for rendering. Even so, the improvement was night and day.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I had also moved the rendering to WebGPU (using &lt;em&gt;drawIndexed&lt;/em&gt; to draw thousands of triangles), it could theoretically reach thousands of frames per second – but the monitor only refreshes at 60 Hz, so there’s no point.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;So How Much Did AI Actually Help?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Generating the initial code&lt;/strong&gt; – yes, it was fast, but that was the “just works” version.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writing the WGSL syntax&lt;/strong&gt; – I asked it to produce a compute shader template, and it did – but &lt;strong&gt;once something broke, the AI had zero understanding of WebGPU’s binding‑group layout errors&lt;/strong&gt;, because it lacks runtime context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solving the actual problem&lt;/strong&gt; – I had to read the WebGPU spec three times and dig through Chrome’s &lt;em&gt;about://gpu&lt;/em&gt; error logs myself.
AI is a great junior dev – but when you hit low‑level API pitfalls, it’s as blind as you are.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What This Story Taught Me&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What WebGPU truly changes isn’t “graphics quality” – it’s “computational capacity.”&lt;/strong&gt; Even for 2D games, moving physics/particle logic to the GPU can boost performance several‑fold.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In 2026, HTML game development is no longer about “knowing Canvas” – it’s about “knowing how to program the GPU.”&lt;/strong&gt; Game engines will eventually abstract this, but if you want to squeeze every last drop of performance, you have to understand compute shaders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI writes code impressively, but it won’t step into the pit for you.&lt;/strong&gt; The real value of a developer lies precisely in being able to crawl out after falling in.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;Finally, I Won’t Talk About WebXR&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because I know if I started rambling about how “WebXR brings the metaverse into the browser,” I’d be back in empty‑rhetoric territory.&lt;/p&gt;

&lt;p&gt;So I’ll just leave you with one question:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Have you ever run into a “CPU can’t keep up” wall while building an HTML game? If so, what did you do about it?&lt;/code&gt;&lt;br&gt;
If you’re also considering WebGPU, I can put together a small repository with the complete runnable code (WGSL + JS bindings) from this article and share it with you. Now that would be real meat. 😊&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webgpu</category>
      <category>html</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>How PWA (Progressive Web Apps) are Changing the Mobile Gaming Landscape</title>
      <dc:creator>no momo</dc:creator>
      <pubDate>Wed, 15 Jul 2026 03:12:52 +0000</pubDate>
      <link>https://dev.to/no_momo/how-pwa-progressive-web-apps-are-changing-the-mobile-gaming-landscape-444g</link>
      <guid>https://dev.to/no_momo/how-pwa-progressive-web-apps-are-changing-the-mobile-gaming-landscape-444g</guid>
      <description>&lt;p&gt;For over a decade, mobile game distribution has been held in a tight duopoly. If you wanted to get your mobile game in front of users, you had to play by the rules of the Apple App Store or Google Play Store. This meant giving up a 30% cut of your revenue, navigating opaque and tedious review processes, and forcing users to undergo the high-friction process of searching, downloading, and installing massive binary packages.&lt;/p&gt;

&lt;p&gt;However, a quiet revolution is taking place under the hood of modern mobile browsers. Progressive Web Apps (PWAs) are bridging the gap between web convenience and native app capabilities.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore how PWAs are changing the mobile gaming landscape, the core APIs making it possible, and why the future of casual mobile gaming might live entirely outside the traditional App Store.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is a PWA in the Context of Gaming?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At its core, a PWA is a website that uses modern web capabilities to deliver an app-like experience. For a game, this means it can be installed on a user’s home screen, run in full-screen mode without the browser UI, and—most importantly—work offline.&lt;/p&gt;

&lt;p&gt;To turn a standard HTML5 game into a PWA, developers rely on three main pillars:&lt;/p&gt;

&lt;p&gt;HTTPS: Secure delivery of assets.&lt;br&gt;
Web App Manifest: A JSON file that defines how the game looks on the home screen (icons, orientation, display mode).&lt;br&gt;
Service Workers: Scripts running in the background that handle asset caching and network requests.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Core Tech: Service Workers &amp;amp; Offline Caching&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The biggest historical knock against browser games was that they required a persistent internet connection. If a user entered a subway tunnel or lost cellular signal, the game would crash or fail to load.&lt;/p&gt;

&lt;p&gt;Service Workers solve this by acting as a network proxy. They intercept requests and can serve cached assets instantly, enabling true offline gameplay.&lt;/p&gt;

&lt;p&gt;Here is a simplified example of how a game’s Service Worker pre-caches assets during the installation phase, ensuring the game loads instantly even when offline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sw.js - Game Asset Service Worker&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CACHE_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pixel-runner-v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CRITICAL_ASSETS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/index.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/css/game.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/js/game.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/assets/spritesheet.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/assets/jump_sound.mp3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// Perform installation and cache critical assets&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;install&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CACHE_NAME&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Opened cache, storing game assets...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CRITICAL_ASSETS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Intercept fetch requests and serve from cache if available&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Cache hit - return response, otherwise fall back to network&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Combined with a Web App Manifest, your HTML5 game can be installed directly from Safari or Chrome onto the user’s home screen, hiding the URL bar and browser navigation buttons to provide a truly immersive, native feel.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Disruption of the App Store Monopolies&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Beyond technical features, the rise of PWAs is a major strategic shift for indie game developers.&lt;/p&gt;

&lt;p&gt;Bypassing the “App Store Tax”&lt;/p&gt;

&lt;p&gt;Both Apple and Google charge developers up to 30% for in-app purchases (IAP) and subscriptions. By distributing your game as a PWA, you handle payments directly through web standards (like Stripe or PayPal), keeping 95% to 98% of your revenue.&lt;/p&gt;

&lt;p&gt;Frictionless User Acquisition&lt;/p&gt;

&lt;p&gt;With native apps, every step in the funnel loses users: &lt;br&gt;
Clicking an Ad -&amp;gt; Loading App Store -&amp;gt; Clicking Install -&amp;gt; Waiting for Download -&amp;gt; Opening App.&lt;/p&gt;

&lt;p&gt;With a PWA, the funnel is practically non-existent: &lt;br&gt;
Clicking an Ad -&amp;gt; Game Loads Instantly.&lt;/p&gt;

&lt;p&gt;Once the user is playing and enjoying the game, a subtle in-game banner can prompt them to “Add to Home Screen” to install it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Hardware Integration: Closing the Gap&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Modern browsers have granted the web platform access to device hardware that was once exclusive to native apps. Today, PWA games can leverage:&lt;/p&gt;

&lt;p&gt;Pointer Lock &amp;amp; Gamepad APIs: Full keyboard/mouse lock and native game controller support (Xbox, PlayStation controllers) are supported natively in the browser.&lt;br&gt;
Vibration API: Standard haptic feedback can be triggered on mobile devices to enhance gameplay feel during explosions or impacts.&lt;br&gt;
Web Audio API: High-fidelity, low-latency audio synthesis and spatial audio can be processed directly inside the web browser.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Challenges: What’s Still Missing?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While PWAs are revolutionary, they are not a silver bullet. iOS, in particular, has historically been slower to adopt progressive web standards compared to Android, occasionally limiting background push notifications or local storage limits for web applications.&lt;/p&gt;

&lt;p&gt;Additionally, discoverability is a double-edged sword. While you are free from App Store gatekeepers, you also lose the organic search traffic that comes from being featured on the App Store front page. Developers must rely on SEO, social media, and word-of-mouth virality to drive users to their URLs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Progressive Web Apps are democratizing game distribution. By transforming the web browser into an incredibly powerful runtime environment, PWAs allow indie developers to build highly optimized, instant-play, and offline-capable games that run globally on any device.&lt;/p&gt;

&lt;p&gt;As app store models continue to face legal scrutiny and user fatigue grows, the web is reclaiming its place as the ultimate open gaming platform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What do you think?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Have you tried packing your web games as PWAs? How do you handle local storage or asset updates? Let’s share some insights in the comments!&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>webdev</category>
      <category>html</category>
      <category>javascript</category>
    </item>
    <item>
      <title>WebGL vs. Canvas 2D: Which One Should You Choose for Your Next Browser Game?</title>
      <dc:creator>no momo</dc:creator>
      <pubDate>Tue, 14 Jul 2026 08:11:27 +0000</pubDate>
      <link>https://dev.to/no_momo/webgl-vs-canvas-2d-which-one-should-you-choose-for-your-next-browser-game-2273</link>
      <guid>https://dev.to/no_momo/webgl-vs-canvas-2d-which-one-should-you-choose-for-your-next-browser-game-2273</guid>
      <description>&lt;p&gt;When you start building a new browser game, one of the very first technical decisions you must make is choosing your rendering context. Under the hood of the HTML5  element lies a fork in the road: do you call canvas.getContext('2d') or canvas.getContext('webgl')?&lt;/p&gt;

&lt;p&gt;While it is tempting to think “newer and faster is always better,” the reality of game development is governed by constraints—time, complexity, bundle size, and target audience.&lt;/p&gt;

&lt;p&gt;In this article, we’ll break down the architectural differences between Canvas 2D and WebGL, compare their performance, and help you decide which rendering path to take for your next web game project.&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%2F4gtsgvzfke33eckschq3.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%2F4gtsgvzfke33eckschq3.png" alt=" " width="800" height="707"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Canvas 2D: The Reliable, CPU-Bound Workhorse&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Canvas 2D API has been around for over a decade. It provides a simple, immediate-mode rendering interface where you draw shapes, text, and images directly using JavaScript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Quick Canvas 2D Setup&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gameCanvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Drawing a simple player sprite&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;playerSprite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Pros:&lt;/p&gt;

&lt;p&gt;Low Barrier to Entry: The API is incredibly intuitive. If you want to draw a rectangle, write some text, or rotate an image, you can do it in one or two lines of code.&lt;br&gt;
Built-in Text Rendering: Canvas 2D handles native system fonts and text layout gracefully, which is notoriously difficult to do in pure WebGL.&lt;br&gt;
Tiny Footprint: You don’t need heavy libraries or boilerplate to get started. It’s perfect for vanilla JavaScript prototypes.&lt;br&gt;
The Cons:&lt;/p&gt;

&lt;p&gt;CPU Bottleneck: While modern browsers hardware-accelerate Canvas 2D where possible, it remains heavily dependent on CPU calculations and single-threaded JavaScript execution.&lt;br&gt;
The Sprite Limit: If your game requires rendering more than a few hundred moving sprites, particle effects, or complex screen-wide filters simultaneously, you will likely see your frame rate drop below 60fps.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;WebGL: The Hardware-Accelerated Beast&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;WebGL (Web Graphics Library) is a JavaScript API that interfaces directly with the user’s GPU (Graphics Processing Unit). It is a low-level API based on OpenGL ES, meaning it gives you immense control over the hardware but comes with extreme complexity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// WebGL Initialization is significantly more verbose&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gameCanvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webgl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;experimental-webgl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WebGL not supported by your browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Pros:&lt;/p&gt;

&lt;p&gt;GPU Acceleration: Calculations are offloaded to the GPU, which is parallel-processed. It can handle tens of thousands of sprites, complex shaders, and real-time lighting without breaking a sweat.&lt;br&gt;
True 3D Capabilities: While Canvas 2D is strictly flat, WebGL allows you to build fully immersive 3D worlds, complete with depth buffers and camera matrices.&lt;br&gt;
Custom Shaders: You can write GLSL (OpenGL Shading Language) programs to create stunning visual effects, such as water ripples, heat distortion, and dynamic shadows.&lt;br&gt;
The Cons:&lt;/p&gt;

&lt;p&gt;Extreme Complexity: Writing raw WebGL is notoriously difficult. Creating a simple colored triangle requires dozens of lines of boilerplate code to set up shaders, buffers, and program states.&lt;br&gt;
Asset/Engine Overhead: To make WebGL manageable, most developers rely on third-party frameworks. While powerful, these engines add to your game’s initial bundle size and load times.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Performance &amp;amp; Architecture Comparison&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To understand the difference, imagine drawing a forest of 5,000 trees:&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%2Ft8zso75haqhfhhhm499x.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%2Ft8zso75haqhfhhhm499x.png" alt=" " width="646" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Modern Solution: Hybrid Engines&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Thankfully, in 2026, web game developers rarely have to write raw WebGL. The modern ecosystem has given us powerful abstraction layers that offer the best of both worlds.&lt;/p&gt;

&lt;p&gt;For 2D Games: Use PixiJS&lt;/p&gt;

&lt;p&gt;If you are building a 2D game but want WebGL performance, PixiJS is the industry standard. It is a super-fast 2D WebGL renderer. Under the hood, it uses WebGL for blazing-fast performance but automatically falls back to Canvas 2D if WebGL is disabled or unsupported on the user’s older device.&lt;/p&gt;

&lt;p&gt;For 3D Games: Use Three.js or Babylon.js&lt;/p&gt;

&lt;p&gt;If you are taking the plunge into 3D, libraries like Three.js act as excellent wrappers, giving you access to lighting, materials, and cameras without requiring a degree in matrix mathematics.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Which One Should You Choose?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Choose Canvas 2D if:&lt;/p&gt;

&lt;p&gt;You are building a casual game: Card games, board games, word puzzles (like Wordle), or turn-based strategy games do not require GPU power.&lt;br&gt;
You want to minimize bundle size: If you are targeting instant-play platforms with strict file size limits (like Telegram or WeChat mini-programs), Canvas 2D’s lack of external dependencies is a massive advantage.&lt;br&gt;
You are a beginner: If you are still learning game loops and physics, start here to avoid getting overwhelmed by graphics pipelines.&lt;br&gt;
Choose WebGL if:&lt;/p&gt;

&lt;p&gt;Your game is visually intensive: Fast-paced action platformers, bullet-hell shooters, or games with heavy particle systems (explosions, smoke, rain).&lt;br&gt;
You are building a 3D game: From simple low-poly runners to complex shooters, 3D requires the GPU.&lt;br&gt;
You want to use custom post-processing: If your game’s aesthetic relies on retro CRT screen shaders, bloom, or dynamic lighting.&lt;br&gt;
What’s your stack?&lt;/p&gt;

&lt;p&gt;Are you a raw Canvas purist, or do you always default to a WebGL framework? Let’s talk about your rendering choices and performance optimizations in the comments!&lt;/p&gt;

</description>
      <category>webgl</category>
      <category>canvas</category>
      <category>web3</category>
      <category>html</category>
    </item>
    <item>
      <title>Why Browser Games Are Making a Comeback in 2026</title>
      <dc:creator>no momo</dc:creator>
      <pubDate>Thu, 09 Jul 2026 07:29:17 +0000</pubDate>
      <link>https://dev.to/no_momo/why-browser-games-are-making-a-comeback-in-2026-45pi</link>
      <guid>https://dev.to/no_momo/why-browser-games-are-making-a-comeback-in-2026-45pi</guid>
      <description>&lt;p&gt;If you were around during the golden era of the web, you probably remember Flash. It was the undisputed king of browser gaming, powering legendary portals like Newgrounds and Kongregate. When Flash met its official demise in 2020, many predicted that browser-based gaming would fade into obscurity, replaced entirely by native mobile apps and console ecosystems.&lt;/p&gt;

&lt;p&gt;For a few years, that prediction seemed accurate. Early HTML5 Canvas games often suffered from performance bottlenecks, awkward mobile controls, and limited rendering capabilities.&lt;/p&gt;

&lt;p&gt;However, fast forward to 2026, and we are witnessing a massive, quiet renaissance. Browser games are not just returning; they are thriving. But what changed under the hood, and why is HTML5 Canvas suddenly the darling of modern web developers again?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Beyond the 2D Context: The Technical Leap&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the early 2010s, developers trying to build complex games using the HTML5 Canvas API quickly hit a wall. Rendering thousands of sprites simultaneously on a 2D context resulted in severe frame drops, especially on mobile browsers.&lt;/p&gt;

&lt;p&gt;Today, the technical landscape is entirely different. The evolution of the Canvas element isn’t just about the 2D rendering context; it is about how Canvas acts as the gateway to hardware-accelerated graphics.&lt;/p&gt;

&lt;p&gt;WebAssembly (Wasm) + WebGL/WebGPU&lt;/p&gt;

&lt;p&gt;The modern browser game stack rarely relies on pure JavaScript for heavy calculations. Instead, developers are using C++, Rust, or Go, compiling the game logic into WebAssembly, and rendering it through the Canvas using WebGL or the newly matured WebGPU standard.&lt;/p&gt;

&lt;p&gt;By bypassing the JavaScript garbage collector and running close to native speed, browser games can now handle complex physics, real-time lighting, and 3D environments that were unimaginable a decade ago.&lt;/p&gt;

&lt;p&gt;Here is a look at how modern web engines optimize the rendering loop using requestAnimationFrame to ensure smooth 60fps performance without draining the user’s battery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gameCanvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// or 'webgl' / 'webgpu'&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lastTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;gameLoop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Calculate delta time for frame-rate independent physics&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deltaTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;lastTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;lastTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;updatePhysics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deltaTime&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;renderGraphics&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Request the next frame only when the browser is ready&lt;/span&gt;
    &lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gameLoop&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Start the loop&lt;/span&gt;
&lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gameLoop&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;The Battle Against “App Fatigue”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While technology paved the way, user behavior drove the comeback. In 2026, mobile users are suffering from what industry analysts call “App Fatigue.”&lt;/p&gt;

&lt;p&gt;Downloading a new casual game today is a high-friction experience:&lt;/p&gt;

&lt;p&gt;You must visit an App Store.&lt;br&gt;
You must download 200MB+ of assets over mobile data.&lt;br&gt;
You have to grant various privacy permissions.&lt;br&gt;
Your device storage is constantly full.&lt;br&gt;
Browser-based HTML5 games eliminate this entire pipeline. They offer instant play. You click a link on social media or in a chat app, and within three seconds, you are playing the game. No downloads, no installations, no storage warnings. For casual and mid-core gaming, convenience has triumphed over native performance.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Rise of “Instant-Social” Ecosystems&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Another major driver of the 2026 browser game comeback is the integration of web games into existing communication platforms.&lt;/p&gt;

&lt;p&gt;Ecosystems like Discord Activities, Telegram Mini-Apps, and WeChat Games have built-in web views that rely entirely on HTML5 Canvas. Instead of leaving the chat app to play a game with friends, users can launch a fully functional multiplayer game inside their group chats.&lt;/p&gt;

&lt;p&gt;For developers, this is a massive win. You don’t need to build a user acquisition funnel from scratch; you just deploy your game to the web, and the social platforms handle the virality. High-level frameworks like Phaser and PlayCanvas have adapted to this trend, offering lightweight, modular builds specifically optimized for these instant-play environments.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Future: A Golden Age for Indie Web Devs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The democratization of web standards has made browser game development more accessible than ever. With modern build tools, hot module reloading, and robust deployment platforms, indie developers can build, test, and launch a web game globally in a matter of days.&lt;/p&gt;

&lt;p&gt;We are no longer limited to basic puzzle games. The Canvas has evolved into a highly optimized viewport capable of delivering immersive, multiplayer, and cross-platform experiences directly to anyone with a web browser.&lt;/p&gt;

&lt;p&gt;If you haven’t touched game development yet, there has never been a better time to open up an editor, grab a Canvas context, and start building. The web is ready.&lt;/p&gt;

&lt;p&gt;What are your thoughts?&lt;/p&gt;

&lt;p&gt;Are you currently developing web-based games, or do you still prefer native platforms? What frameworks are you using in 2026? Let’s discuss in the comments below!&lt;/p&gt;

</description>
      <category>html</category>
      <category>gamedev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Optimize Your Routine: The Practical Value of a planetary hours Calculator</title>
      <dc:creator>no momo</dc:creator>
      <pubDate>Wed, 23 Apr 2025 08:38:27 +0000</pubDate>
      <link>https://dev.to/no_momo/optimize-your-routine-the-practical-value-of-a-planetary-hours-calculator-73m</link>
      <guid>https://dev.to/no_momo/optimize-your-routine-the-practical-value-of-a-planetary-hours-calculator-73m</guid>
      <description>&lt;p&gt;Have you ever felt that certain times of the day seem better suited for specific activities? Ancient wisdom suggests this isn't mere coincidence. Welcome to the fascinating world of "&lt;a href="https://planetaryhours.org" rel="noopener noreferrer"&gt;planetary hours&lt;/a&gt;" – a system that divides the day (sunrise to sunset) and the night (sunset to sunrise) each into 12 segments, ruled sequentially by the seven classical planets (Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn).&lt;br&gt;
    Imagine handling communications and writing during a "Mercury Hour," engaging in social or artistic pursuits during a "Venus Hour," or tackling tasks requiring energy during a "Mars Hour." Sound complicated? Don't worry! If you're interested in this ancient timing wisdom, you should definitely try our &lt;a href="https://planetaryhours.org/" rel="noopener noreferrer"&gt;planetary hours Calculator&lt;/a&gt;. It automatically calculates the precise start and end times for each planetary hours based on your location and the current date. No manual calculations needed – with just a click, you can discover which planetary energy is currently dominant, helping you better synchronize with the natural rhythms of the cosmos. Use our planetary hours Calculator now and start aligning your day with cosmic energies! Read also: &lt;a href="https://planetaryhours.org" rel="noopener noreferrer"&gt;https://planetaryhours.org&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
