<?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: Olha Stefanishyna</title>
    <description>The latest articles on DEV Community by Olha Stefanishyna (@olha-stefanishyna).</description>
    <link>https://dev.to/olha-stefanishyna</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3023504%2Fd59caf8c-5948-4cd2-8e42-11baab79e1e6.jpeg</url>
      <title>DEV Community: Olha Stefanishyna</title>
      <link>https://dev.to/olha-stefanishyna</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/olha-stefanishyna"/>
    <language>en</language>
    <item>
      <title>Positive Security Models: From Block-Lists to Allow-Lists</title>
      <dc:creator>Olha Stefanishyna</dc:creator>
      <pubDate>Wed, 09 Jul 2025 07:07:28 +0000</pubDate>
      <link>https://dev.to/olha-stefanishyna/positive-security-models-from-block-lists-to-allow-lists-48c6</link>
      <guid>https://dev.to/olha-stefanishyna/positive-security-models-from-block-lists-to-allow-lists-48c6</guid>
      <description>&lt;p&gt;Hey DEV community! 👋&lt;/p&gt;

&lt;p&gt;Ever wondered why your WAF lets some attacks slip through? Are you trying to block known bad stuff while allowing everything else?&lt;/p&gt;

&lt;p&gt;What if I told you there's a better way? 🛡️&lt;/p&gt;

&lt;p&gt;Positive security at infrastructure and application layers is that better way.&lt;br&gt;
This approach protects against entire classes of attacks, including ones that don't exist yet.&lt;/p&gt;

&lt;p&gt;Ready to flip your security paradigm?&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://ostefani.dev/tech-notes/security-models" rel="noopener noreferrer"&gt;Read the complete article: "Building Fortress-First: The Power of Positive Security Models"&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy securing! 🔒&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>backend</category>
    </item>
    <item>
      <title>Mastering Offscreen Rendering in WebGL</title>
      <dc:creator>Olha Stefanishyna</dc:creator>
      <pubDate>Wed, 18 Jun 2025 11:17:05 +0000</pubDate>
      <link>https://dev.to/olha-stefanishyna/mastering-offscreen-rendering-in-webgl-19e5</link>
      <guid>https://dev.to/olha-stefanishyna/mastering-offscreen-rendering-in-webgl-19e5</guid>
      <description>&lt;p&gt;Hey DEV community! 👋&lt;/p&gt;

&lt;p&gt;Want to create cool post-processing effects in your WebGL projects, like bloom or motion blur? Or maybe you need to render a complex 3D scene to a texture for later use? Working with complex effects relies in understanding &lt;strong&gt;Framebuffers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I've been diving deep into WebGL lately and wanted to share my journey of rendering scenes to a texture. This process is often called  offscreen rendering.&lt;/p&gt;

&lt;p&gt;In my latest article, I break down the entire process. We'll explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What Framebuffers are&lt;/strong&gt; and why they are so essential in modern graphics.&lt;/li&gt;
&lt;li&gt;The process of &lt;strong&gt;setting up your Framebuffer&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A practical example of how to render your scene to a texture and then use that texture on another object.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This technique opens up a whole new world of possibilities for advanced rendering and special effects.&lt;/p&gt;

&lt;p&gt;Ready to take your WebGL skills to the next level?&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://ostefani.dev/tech-notes/rendering-to-texture-with-framebuffers" rel="noopener noreferrer"&gt;Read the full, detailed guide on my blog: "Rendering to Texture with Framebuffers in WebGL"&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I've included code snippets and explanations to make it as clear as possible. Let me know what you think, and I'd love to see what you create with it!&lt;/p&gt;

&lt;p&gt;Happy coding! ✨&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Like my content? Follow me on &lt;a href="https://x.com/o_stefanishyna" rel="noopener noreferrer"&gt;X&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>webgl2</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>graphics</category>
    </item>
    <item>
      <title>Timing Attacks: Why Your Code Might Be Leaking Secrets</title>
      <dc:creator>Olha Stefanishyna</dc:creator>
      <pubDate>Tue, 10 Jun 2025 04:17:07 +0000</pubDate>
      <link>https://dev.to/olha-stefanishyna/timing-attacks-why-your-code-might-be-leaking-secrets-5f9n</link>
      <guid>https://dev.to/olha-stefanishyna/timing-attacks-why-your-code-might-be-leaking-secrets-5f9n</guid>
      <description>&lt;p&gt;Have you ever heard about &lt;strong&gt;side-channel attacks&lt;/strong&gt;? They are a category of security attacks where attackers instead of breaking cryptography directly, analyze unintended information that "leaks" from the physical implementation of a system during normal operation.&lt;/p&gt;

&lt;p&gt;Side-channel attacks are dangerous - even perfectly implemented cryptography can be vulnerable because of information leakage from the physical implementation of cryptographic systems. They may be invisible to standard security audits as it requires interdisciplinary knowledge to understand and prevent.&lt;/p&gt;

&lt;p&gt;There are several types of the attack: timing attacks, cache timing attacks, power analysis attacks and others.&lt;/p&gt;

&lt;p&gt;Timing attacks are one of the most common and accessible side-channel attacks because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  They can be performed remotely over networks&lt;/li&gt;
&lt;li&gt;  Don't require physical access to hardware&lt;/li&gt;
&lt;li&gt;  Can be executed with standard software tools&lt;/li&gt;
&lt;li&gt;  Often affect web applications and APIs directly&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding timing attacks
&lt;/h2&gt;

&lt;p&gt;Instead of attacking the mathematical solution of your encryption algorithms, these attacks target how your code actually runs, allowing attackers to extract passwords, private keys, and other sensitive data simply by measuring response times. Timing attacks present unique challenges because they can bypass traditional security measures entirely.&lt;/p&gt;

&lt;p&gt;In 2003, researchers David Brumley and Dan Boneh demonstrated extracting complete 1024-bit RSA private keys from SSL servers over a network using only timing analysis—requiring about one million queries over two hours. Their attack worked despite network jitter and proved that timing attacks are real danger for real systems.&lt;/p&gt;

&lt;p&gt;The core principle is deceptively simple: execution time often correlates with specific properties of the secret like values and length.&lt;/p&gt;

&lt;p&gt;For example, consider this condition:&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HELLO&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AAAA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is technically vulnerable because JavaScript engines (V8, SpiderMonkey) implement string comparison with an early exit for performance.&lt;/p&gt;

&lt;p&gt;Testing "AAAA" against string "HELLO" fails on the first character (fastest), while "HAAA" fails on the second character (slightly slower), and "HEAA" fails on the third (even slower). An attacker can determine the correct string character-by-character, reducing the search space from exponential to linear complexity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is important&lt;/strong&gt; - it is not a practical example because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Timing differences are tiny - nanoseconds, not milliseconds&lt;/li&gt;
&lt;li&gt;  Network jitter drowns out these tiny differences&lt;/li&gt;
&lt;li&gt;  Nobody compares passwords directly (I hope so)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's consider more practical examples.&lt;/p&gt;

&lt;p&gt;Real timing attacks exploit operations that take milliseconds, not nanoseconds. When a database lookup or cryptographic operation creates measurable delays, attackers can extract secrets even over noisy networks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication systems under attack
&lt;/h2&gt;

&lt;p&gt;Authentication systems represent the highest-value target for timing attacks in web applications. When your authentication takes different amounts of time based on whether a username exists, or when your password comparison exits early on the first wrong character, you're broadcasting sensitive information through timing patterns.&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;function&lt;/span&gt; &lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&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;user&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="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Fast return - major timing leak&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;bcrypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hashedPassword&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Slow operation&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When an invalid username is provided, the function returns immediately. For valid usernames, the expensive &lt;code&gt;bcrypt.compare()&lt;/code&gt; operation executes, creating a timing difference that's easily detectable across network connections.&lt;/p&gt;

&lt;p&gt;Attackers can build complete lists of valid usernames before launching targeted password attacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  API rate limiting and timing analysis
&lt;/h2&gt;

&lt;p&gt;Rate limiting systems, designed to prevent abuse, often become timing attack vectors themselves. &lt;strong&gt;Timing-based rate limit detection&lt;/strong&gt; allows attackers to map rate limiting thresholds and develop evasion strategies:&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;// Rate limiting creates timing patterns&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;rateLimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;windowMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&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;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 15 minutes&lt;/span&gt;
        &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Limit requests per window&lt;/span&gt;
        &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Too many requests&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Immediate response&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;// Normal request: 200-500ms response&lt;/span&gt;
&lt;span class="c1"&gt;// Rate limited request: &amp;lt;10ms response (timing leak)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rate-limited requests typically return immediately with error codes, while normal requests undergo full processing. This timing difference enables attackers to detect rate limiting activation and adjust their attack patterns accordingly.&lt;/p&gt;

&lt;p&gt;What to do with the attack on the rate limiting systems? You may see suggestions like hiding rate limits.&lt;br&gt;
Because visible rate limiting allows attackers to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Map exact thresholds (e.g., "I can make exactly 99 requests before hitting the limit")&lt;/li&gt;
&lt;li&gt;  Optimize attack patterns to stay just under limits&lt;/li&gt;
&lt;li&gt;  Rotate through different IPs/sessions more efficiently&lt;/li&gt;
&lt;li&gt;  Time their attacks to reset windows perfectly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea is that uncertainty forces attackers to be more cautious. However, this is generally considered "security through obscurity" and not very effective - attackers can still discover the limits through probing.&lt;/p&gt;

&lt;p&gt;I would suggest to consider rate limits as public information. They just should be sensible, but they may not be secrets.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You may learn more about rate-limiting at (Backend Rate Limiting)[&lt;a href="https://dev.to/olha-stefanishyna/rate-limiting-architectural-layers-in-web-applications-3g76"&gt;https://dev.to/olha-stefanishyna/rate-limiting-architectural-layers-in-web-applications-3g76&lt;/a&gt;]&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Modern defensive strategies and countermeasures
&lt;/h2&gt;

&lt;p&gt;Defending against timing attacks requires a multi-layered approach combining &lt;strong&gt;constant-time programming&lt;/strong&gt;, &lt;strong&gt;architectural defenses&lt;/strong&gt;, and &lt;strong&gt;monitoring systems&lt;/strong&gt;. The fundamental principle is ensuring that execution time remains independent of secret data.&lt;/p&gt;
&lt;h3&gt;
  
  
  Constant-time comparison functions
&lt;/h3&gt;

&lt;p&gt;Constant-time comparison functions are the cornerstone of timing attack defense. Modern platforms provide built-in timing-safe comparison functions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dummy‐hash trick for passwords&lt;/strong&gt;:&lt;br&gt;
Make your login endpoint always invoke the slow password check, whether the username exists or not, so an attacker can’t time‐probe “does this user exist?”&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;//  Precompute a dummy hash once at startup:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DUMMY_HASH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$2b$12$K9QhMWzVN5YbRz5sXl0ueODhk0PtAyp7cVt2hx6Vj7XOx0JPTWm6W&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;//  In your login handler, always do bcrypt.compare:&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// If user is missing, compare against the dummy hash&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hashToCompare&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DUMMY_HASH&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;passwordValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hashToCompare&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;user&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;passwordValid&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="na"&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;Invalid credentials&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generateToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&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;Bcrypt’s &lt;code&gt;.compare()&lt;/code&gt; is already designed to run in constant time per hash, but if you ever compare raw secrets (HMACs, tokens, API keys, etc.), you should never use &lt;code&gt;===&lt;/code&gt;. Instead do something like&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;timingSafeEqual&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;crypto&lt;/span&gt;&lt;span class="dl"&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;safeCompare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&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;bufA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&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;bufB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// If lengths differ, compare bufA to itself (constant time)&lt;/span&gt;
    &lt;span class="c1"&gt;// to prevent leaking the length of the secret&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bufA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;bufB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;timingSafeEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bufA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bufA&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// true constant-time compare&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;timingSafeEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bufA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bufB&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;h3&gt;
  
  
  Architectural defenses
&lt;/h3&gt;

&lt;p&gt;Architectural defenses are system design patterns that eliminate timing attack vectors by restructuring how your application processes sensitive operations. Instead of fixing timing leaks in code, you redesign the system so timing information becomes meaningless to attackers.&lt;/p&gt;

&lt;p&gt;Key Architectural Patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Request/Response Decoupling&lt;/li&gt;
&lt;li&gt;  Response Caching Strategy&lt;/li&gt;
&lt;li&gt;  Uniform Processing Pipelines&lt;/li&gt;
&lt;li&gt;  Service Segregation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look closer at Request/Response Decoupling:&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;// app/api/auth/forgot-password/route.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;addToQueue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/lib/queue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&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;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Queue the task instead of processing inline&lt;/span&gt;
    &lt;span class="nf"&gt;addToQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password-reset&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;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Return response immediately&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;If an account exists, you'll receive reset instructions&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="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/** Background worker processes queue (runs separately) **/&lt;/span&gt;
&lt;span class="c1"&gt;// lib/workers/password-reset.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processPasswordReset&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;findUserByEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&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="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendPasswordResetEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// No timing information leaks to the original requester&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Monitoring systems
&lt;/h3&gt;

&lt;p&gt;Monitoring systems are detection and analysis tools that identify timing attack attempts by analyzing request patterns, response times, and behavioral anomalies. Unlike rate limiting systems that prevent requests, monitoring systems are designed for detection. They observe and alert on suspicious patterns without blocking the traffic, allowing you to analyze potential attacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start protecting your code today
&lt;/h2&gt;

&lt;p&gt;Defending against timing attacks starts with three simple changes to your codebase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Switch all secret comparisons to timing-safe functions&lt;/li&gt;
&lt;li&gt;  Implement dummy processing for authentication failures&lt;/li&gt;
&lt;li&gt;  Use libraries with built-in timing attack protection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Timing attacks are subtle but real. They won't show up in your unit tests or security scans. The best defense is understanding how they work and building protection into your code from the start by incorporating timing attack considerations into architectural decisions, development processes, and monitoring systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;While the fundamental attack principles remain unchanged since Paul Kocher's pioneering work in 1996, timing attacks now affect a much broader range of applications - from browser JavaScript to backend services.&lt;/p&gt;

&lt;p&gt;Success in defending against timing attacks requires combining multiple defensive layers: constant-time implementations at the code level, architectural protections like rate limiting and response normalization, comprehensive monitoring for attack detection, and regular security assessments to identify emerging vulnerabilities. By implementing these defenses systematically, development teams can build applications that resist even sophisticated timing attacks while maintaining the performance and functionality that users expect.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>javascript</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>WebGL 2 Basics: Drawing a Full-Screen Quad</title>
      <dc:creator>Olha Stefanishyna</dc:creator>
      <pubDate>Fri, 06 Jun 2025 12:47:12 +0000</pubDate>
      <link>https://dev.to/olha-stefanishyna/webgl-2-basics-drawing-a-full-screen-quad-3fcd</link>
      <guid>https://dev.to/olha-stefanishyna/webgl-2-basics-drawing-a-full-screen-quad-3fcd</guid>
      <description>&lt;p&gt;As mentioned in the &lt;a href="https://dev.to/olha-stefanishyna/getting-started-with-webgl-2-conceptual-foundation-33p6"&gt;previous article&lt;/a&gt;, WebGL 2 uses a programmable pipeline.&lt;br&gt;
This article covers shaders and GLSL language fundamentals - the essential building blocks before tackling fluid simulation.&lt;/p&gt;

&lt;p&gt;Before creating complex effects, you need to understand how WebGL fundamentally renders anything to the screen. Unlike declarative approaches like HTML/CSS, WebGL requires to explicitly define the geometry and the appearance using custom programs. These programs, called shaders, are written in GLSL and executed directly on the GPU.&lt;/p&gt;
&lt;h2&gt;
  
  
  Introduction to GLSL ES 3.0
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GLSL ES 3.0&lt;/strong&gt; (OpenGL Shading Language for Embedded Systems version 3.0) is a high-level shading language with a C-like syntax, specifically designed for graphics processing units (GPUs) in embedded systems and web browsers.&lt;br&gt;
The GLSL ES 3.00 specification requires every shader to begin with &lt;code&gt;#version 300 es&lt;/code&gt; to specify GLSL version for Embedded Systems. Without this directive, the shader won't compile. It also requires precision qualifiers in fragment shaders (e.g., &lt;code&gt;precision mediump float;&lt;/code&gt;)&lt;/p&gt;
&lt;h3&gt;
  
  
  GLSL ES 3.0 Type System
&lt;/h3&gt;

&lt;p&gt;Type System is the building blocks of GLSL ES 3.0 code. Understanding the type system will help declare variables correctly and use built-in functions without confusion.&lt;/p&gt;
&lt;h4&gt;
  
  
  Scalar Types
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;float&lt;/code&gt; (32-bit IEEE 754)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;int&lt;/code&gt; (32-bit signed)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;uint&lt;/code&gt; (32-bit unsigned)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;bool&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the simplest types: single numeric or boolean values.&lt;/p&gt;
&lt;h4&gt;
  
  
  Vector Types
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  Floating-point: &lt;code&gt;vec2&lt;/code&gt;, &lt;code&gt;vec3&lt;/code&gt;, &lt;code&gt;vec4&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Integer: &lt;code&gt;ivec2&lt;/code&gt;, &lt;code&gt;ivec3&lt;/code&gt;, &lt;code&gt;ivec4&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Unsigned: &lt;code&gt;uvec2&lt;/code&gt;, &lt;code&gt;uvec3&lt;/code&gt;, &lt;code&gt;uvec4&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Boolean: &lt;code&gt;bvec2&lt;/code&gt;, &lt;code&gt;bvec3&lt;/code&gt;, &lt;code&gt;bvec4&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GPUs process vector operations efficiently in parallel.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="kt"&gt;vec4&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&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="mi"&gt;5&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;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// Same as color.x or color[0]&lt;/span&gt;
&lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;bgr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bgr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Swizzle: vec3(0.2, 0.5, 1.0)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Matrix and Sampler Types
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  Matrices: &lt;code&gt;mat2&lt;/code&gt;, &lt;code&gt;mat3&lt;/code&gt;, &lt;code&gt;mat4&lt;/code&gt; (plus non-square variants)&lt;/li&gt;
&lt;li&gt;  Texture samplers: &lt;code&gt;sampler2D&lt;/code&gt;, &lt;code&gt;sampler3D&lt;/code&gt;, &lt;code&gt;samplerCube&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Integer samplers: &lt;code&gt;isampler2D&lt;/code&gt;, &lt;code&gt;usampler2D&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A sampler type (e.g. &lt;code&gt;sampler2D&lt;/code&gt;) tells the compiler you want to sample a 2D texture inside your shader. You’ll see these used in fragment shaders when you write expressions like &lt;code&gt;texture(uTexture, vUV)&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Variable Qualifiers
&lt;/h3&gt;

&lt;p&gt;In GLSL, you also need to tell the compiler how data moves into and out of each shader stage. That’s where qualifiers like &lt;code&gt;in&lt;/code&gt;, &lt;code&gt;out&lt;/code&gt;, and &lt;code&gt;uniform&lt;/code&gt; come in.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;in&lt;/code&gt;: Input data (per-vertex in vertex shader, interpolated in fragment shader)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;out&lt;/code&gt;: Output data (interpolated outputs in vertex shader, framebuffer outputs in fragment shader)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;uniform&lt;/code&gt;: Constant values for entire draw call&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Uniform Blocks
&lt;/h4&gt;

&lt;p&gt;WebGL 2 supports grouping uniforms into blocks for efficient updates.&lt;br&gt;
The GLSL code declares the uniform block structure in the shader:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="k"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std140&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="n"&gt;TransformBlock&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;mat4&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;mat4&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;mat4&lt;/span&gt; &lt;span class="n"&gt;projection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JavaScript code creates a buffer and uploads data to match that structure:&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;// Create and bind uniform buffer&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ubo&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="nf"&gt;createBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bindBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UNIFORM_BUFFER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ubo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bufferData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UNIFORM_BUFFER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transformData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DYNAMIC_DRAW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Associate with shader program&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blockIndex&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="nf"&gt;getUniformBlockIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TransformBlock&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniformBlockBinding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;blockIndex&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;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bindBufferBase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UNIFORM_BUFFER&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;ubo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;std140&lt;/code&gt; layout is a standardized memory layout rule. It ensures all GPUs organize uniform buffer data the same way. Without this standard, your code might work on one graphics card but fail on another due to different memory arrangements.&lt;/p&gt;

&lt;h3&gt;
  
  
  WebGL 2 Vertex Specification Enhancements
&lt;/h3&gt;

&lt;p&gt;WebGL 2 (GLSL ES 3.0) added several conveniences that cut down on boilerplate JavaScript and reduce errors when setting up vertex data.&lt;/p&gt;

&lt;h4&gt;
  
  
  Vertex Array Objects (VAOs)
&lt;/h4&gt;

&lt;p&gt;Instead of binding and configuring each attribute (position, normal, UV, etc.) every time you draw, you can store that entire setup in a VAO.&lt;/p&gt;

&lt;p&gt;VAOs encapsulate vertex attribute configuration, storing buffer bindings, attribute pointers, and enable states. This eliminates repetitive setup calls:&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;vao&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="nf"&gt;createVertexArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bindVertexArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vao&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Configure all attributes once&lt;/span&gt;
&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bindVertexArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Later: single call to use&lt;/span&gt;
&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bindVertexArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vao&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawArrays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TRIANGLES&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;vertexCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Explicit Attribute Locations
&lt;/h4&gt;

&lt;p&gt;Instead of querying attribute indices at runtime with &lt;code&gt;gl.getAttribLocation&lt;/code&gt;, you can hard-code the location right in your shader.&lt;/p&gt;

&lt;p&gt;GLSL ES 3.0 supports explicit attribute location assignment, eliminating the need for &lt;code&gt;getAttribLocation&lt;/code&gt; queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="k"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&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="k"&gt;in&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;aPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;aColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JavaScript can then reference attributes numerically: &lt;code&gt;gl.enableVertexAttribArray(0)&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shaders
&lt;/h3&gt;

&lt;p&gt;A shader is a program written in GLSL for processing graphics data. There are two main types of shaders that work together to render graphics: the vertex shader and the fragment shader.&lt;/p&gt;

&lt;p&gt;GLSL provides predefined built-in variables that handle communication between shader stages and the GPU.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vertex Shader Built-ins:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;gl_Position&lt;/code&gt;
Must be written to the vertex shader. It’s the clip-space (x, y, z, w) output.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;gl_VertexID&lt;/code&gt;
Automatically provides the index of the current vertex (useful for procedural geometry).&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;gl_InstanceID&lt;/code&gt;
When doing instanced draws, this tells you which instance you’re on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fragment Shader Built-ins:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;gl_FragCoord&lt;/code&gt;
The window-space coordinates &lt;code&gt;(x, y, z, w)&lt;/code&gt; of the current fragment center, where &lt;code&gt;z&lt;/code&gt; is depth and &lt;code&gt;w&lt;/code&gt; is &lt;code&gt;1/w&lt;/code&gt; from clip space. &lt;code&gt;gl_FragCoord.y&lt;/code&gt; uses bottom-left origin (OpenGL convention), so manual flipping may be needed for top-left origin coordinates.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;gl_FrontFacing&lt;/code&gt;
A boolean telling you whether the primitive was front-facing or back-facing.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;gl_FragDepth&lt;/code&gt;
Allows the fragment shader to write a custom value to the depth buffer&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Vertex Shader
&lt;/h4&gt;

&lt;p&gt;The vertex shader executes once per vertex with mandatory output &lt;code&gt;gl_Position&lt;/code&gt; in clip-space coordinates. It receives per-vertex data through &lt;code&gt;in&lt;/code&gt; variables and passes interpolated data to the fragment shader via &lt;code&gt;out&lt;/code&gt; variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="cp"&gt;#version 300 es
&lt;/span&gt;
&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;aPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Per-vertex position from buffer&lt;/span&gt;
&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;aColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// Per-vertex color from buffer&lt;/span&gt;
&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;vColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// Interpolated color for fragments&lt;/span&gt;

&lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="kt"&gt;mat4&lt;/span&gt; &lt;span class="n"&gt;uModelViewMatrix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// Constant for all vertices&lt;/span&gt;
&lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="kt"&gt;mat4&lt;/span&gt; &lt;span class="n"&gt;uProjectionMatrix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// Constant for all vertices&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Transform to clip space with w=1.0 for positions&lt;/span&gt;
    &lt;span class="nb"&gt;gl_Position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uProjectionMatrix&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;uModelViewMatrix&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aPosition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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;vColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Pass color to fragment shader&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After vertex shader execution, the GPU clips primitives outside the clip volume and then the GPU's fixed-function hardware automatically performs perspective division (dividing by the w component) to convert clip-space to Normalized Device Coordinates (NDC).&lt;/p&gt;

&lt;p&gt;Compute in vertex shader when possible — few vertices in the vertex shader vs millions of pixels in the fragment shader.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fragment Shader
&lt;/h4&gt;

&lt;p&gt;After NDC producing, the GPU’s fixed-function maps NDC to window (pixel) coordinates. The rasterizer then converts triangles (defined by vertices) into fragments. Each fragment corresponds to a pixel location that the triangle covers. It contains: screen position (x, y), depth value (z), other interpolated values from vertex shader outputs.&lt;/p&gt;

&lt;p&gt;The fragment shader runs once per fragment, receiving interpolated values from the vertex shader. The GPU automatically interpolates all vertex shader outputs across each triangle's surface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="cp"&gt;#version 300 es
&lt;/span&gt;&lt;span class="k"&gt;precision&lt;/span&gt; &lt;span class="kt"&gt;mediump&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;vColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// Interpolated from vertex shader&lt;/span&gt;
&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt; &lt;span class="n"&gt;fragColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// RGBA output to framebuffer&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fragColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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="c1"&gt;// Alpha = 1.0 for opaque&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output colors typically use a vec4 data type, representing &lt;code&gt;red&lt;/code&gt;, &lt;code&gt;green&lt;/code&gt;, &lt;code&gt;blue&lt;/code&gt;, and &lt;code&gt;Alpha&lt;/code&gt; (RGBA) components, with values often ranging from 0.0 to 1.0.&lt;/p&gt;

&lt;p&gt;GPUs can trade accuracy for speed — using &lt;code&gt;mediump&lt;/code&gt; instead of &lt;code&gt;highp&lt;/code&gt; can improve performance on mobile devices while maintaining acceptable visual quality.&lt;/p&gt;

&lt;p&gt;GPUs execute fragments in groups of threads (sometimes called warps or wavefronts). If a branch splits those threads — some go one way, some go another — the GPU ends up running both sides of the branch. That extra work makes branching less efficient:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Efficient: branchless operations&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;step&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;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Returns 0.0 or 1.0&lt;/span&gt;
&lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;colorA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colorB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Inefficient: dynamic branching&lt;/span&gt;
&lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;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;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;colorB&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;colorA&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Full-Screen Quad Geometry
&lt;/h3&gt;

&lt;p&gt;Many advanced GPU effects — from motion blur to fluid dynamics — are often implemented using two triangles that define a quad. These two triangles form a rectangle, invoking the fragment shader — once for every pixel. If it's full-screen, we call this a 'full-screen quad'. This technique allows you to run a fragment shader on every pixel of the screen or the quad's area.&lt;/p&gt;

&lt;p&gt;A full-screen quad typically uses two triangles to cover the viewport because GPUs are highly optimized for processing triangles. This method ensures the entire rectangular area is consistently covered:&lt;/p&gt;

&lt;h2&gt;
  
  
  Full-Screen Quad Implementation &lt;a id="full-screen-quad-implementation"&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This implementation renders a full-screen quad. We'll create a visual test pattern by outputting UV coordinates as colors - this technique helps verify that our geometry covers the viewport correctly and that our coordinate mapping works as expected. This same setup will later serve as the foundation for post-processing effects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shaders
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Vertex Shader&lt;/span&gt;
&lt;span class="cp"&gt;#version 300 es
&lt;/span&gt;&lt;span class="k"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&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="k"&gt;in&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;aPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;vUV&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;gl_Position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aPosition&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="mi"&gt;1&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="c1"&gt;// Account for WebGL's bottom-left origin:&lt;/span&gt;
    &lt;span class="n"&gt;vUV&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aPosition&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="mi"&gt;5&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="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Flip Y for top-left texture origin&lt;/span&gt;
    &lt;span class="n"&gt;vUV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;vUV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Fragment Shader&lt;/span&gt;
&lt;span class="cp"&gt;#version 300 es
&lt;/span&gt;&lt;span class="k"&gt;precision&lt;/span&gt; &lt;span class="kt"&gt;mediump&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;vUV&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt; &lt;span class="n"&gt;fragColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fragColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vUV&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;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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="c1"&gt;// RG gradient visualization&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  JavaScript Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Context acquisition&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;webgl2&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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 2 not supported&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Shader compilation&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;compileShader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&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;shader&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="nf"&gt;createShader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shaderSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compileShader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shader&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="nf"&gt;getShaderParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COMPILE_STATUS&lt;/span&gt;&lt;span class="p"&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;info&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="nf"&gt;getShaderInfoLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shader&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleteShader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shader&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Compile failed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&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="nx"&gt;shader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Program creation&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createProgram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vertSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fragSrc&lt;/span&gt;&lt;span class="p"&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;program&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="nf"&gt;createProgram&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attachShader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;compileShader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vertSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VERTEX_SHADER&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attachShader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;compileShader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fragSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FRAGMENT_SHADER&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;linkProgram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;program&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="nf"&gt;getProgramParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LINK_STATUS&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Link failed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getProgramInfoLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&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="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Full-screen quad VAO&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createFullScreenQuad&lt;/span&gt;&lt;span class="p"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vao&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="nf"&gt;createVertexArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bindVertexArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vao&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Two triangles: [-1,-1] to [1,1]&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vertices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Float32Array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Triangle 1&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Triangle 2&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;buffer&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="nf"&gt;createBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bindBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ARRAY_BUFFER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bufferData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ARRAY_BUFFER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vertices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STATIC_DRAW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enableVertexAttribArray&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;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vertexAttribPointer&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;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&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;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bindVertexArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&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;vao&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;This implementation renders a full-screen quad with a UV coordinate gradient, serving as the foundation for post-processing effects and GPU-based computations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;: &lt;a href="https://github.com/ostefani/web-gl-series/tree/main/article-2" rel="noopener noreferrer"&gt;repo&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging Techniques
&lt;/h2&gt;

&lt;p&gt;Debugging shaders presents unique challenges because they execute on the GPU, separate from the JavaScript environment. You can't simply use &lt;code&gt;console.log()&lt;/code&gt; inside a GLSL shader to inspect values. So, you have to rely on specific techniques to understand program flow and data, helping to identify and fix issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shader Compilation Checks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="nf"&gt;getShaderParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COMPILE_STATUS&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;Shader error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getShaderInfoLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shader&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;h3&gt;
  
  
  Visual Debugging
&lt;/h3&gt;

&lt;p&gt;Output intermediate values as colors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="n"&gt;fragColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vUV&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="mi"&gt;1&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="c1"&gt;// UV coordinates&lt;/span&gt;
&lt;span class="n"&gt;fragColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vNormal&lt;/span&gt;&lt;span class="p"&gt;)&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="mi"&gt;5&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="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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="c1"&gt;// Normals&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Full-screen quads provide the foundation for GPU image processing and simulations. Two triangles covering the viewport create a one-to-one fragment-to-pixel mapping, enabling parallel computation across the framebuffer. This technique underlies post-processing effects, fluid simulations, and GPU-accelerated algorithms.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is part of my series on implementing interactive 3D visualizations with WebGL 2&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>webgl</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Getting Started with WebGL 2: Conceptual foundation</title>
      <dc:creator>Olha Stefanishyna</dc:creator>
      <pubDate>Wed, 04 Jun 2025 16:32:14 +0000</pubDate>
      <link>https://dev.to/olha-stefanishyna/getting-started-with-webgl-2-conceptual-foundation-33p6</link>
      <guid>https://dev.to/olha-stefanishyna/getting-started-with-webgl-2-conceptual-foundation-33p6</guid>
      <description>&lt;p&gt;Learning WebGL 2 can be challenging, but understanding its core concepts makes the journey easier. This introduction covers the essential concepts you need before writing your first WebGL program.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is WebGL 2?
&lt;/h2&gt;

&lt;p&gt;WebGL 2 (Web Graphics Library) is a JavaScript API that allows rendering interactive 2D and 3D graphics in compatible web browsers without requiring plugins. It's based on OpenGL ES (Embedded Systems), specifically designed for mobile devices and web applications.&lt;/p&gt;

&lt;p&gt;This is fundamentally different from traditional DOM manipulation. While DOM operations primarily work with elements in a tree structure, WebGL 2 provides low-level access to your graphics card through the canvas element, allowing for hardware-accelerated graphics rendering.&lt;/p&gt;

&lt;p&gt;Here's a simple example of initializing a WebGL 2 context:&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;// Get the canvas element&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;myCanvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize WebGL 2 context&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;webgl2&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 2 not supported or enabled on this browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Fallback or error message&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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;WebGL 2 initialized successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Continue with your WebGL 2 code&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To better understand the difference in complexity between these APIs, let's compare what it takes to draw a simple red square in Canvas 2D and WebGL 2:&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;// CANVAS 2D API - Drawing a red square (3 lines of code)&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="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Set the color to red&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;fillRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Draw a 100x100 square at position (10,10)&lt;/span&gt;

&lt;span class="c1"&gt;// WEBGL 2 - Drawing a red square (simplified, but still ~30 lines minimum)&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;webgl2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Define vertex shader (runs once per vertex)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vertexShaderSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`#version 300 es
in vec4 a_position;
void main() {
  gl_Position = a_position;
}`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Define fragment shader (runs once per pixel)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fragmentShaderSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`#version 300 es
precision highp float;
out vec4 outColor;
void main() {
  outColor = vec4(1.0, 0.0, 0.0, 1.0);  // Red color
}`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 3. Create and compile shaders&lt;/span&gt;
&lt;span class="c1"&gt;// ... (multiple lines of setup code omitted)&lt;/span&gt;

&lt;span class="c1"&gt;// 4. Create program and link shaders&lt;/span&gt;
&lt;span class="c1"&gt;// ... (more setup code omitted)&lt;/span&gt;

&lt;span class="c1"&gt;// 5. Define geometry for a square&lt;/span&gt;
&lt;span class="c1"&gt;// ... (more setup code omitted)&lt;/span&gt;

&lt;span class="c1"&gt;// 6. Finally draw&lt;/span&gt;
&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clearColor&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.0&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;1.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Clear to black&lt;/span&gt;
&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COLOR_BUFFER_BIT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useProgram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bindVertexArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vao&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawArrays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TRIANGLE_STRIP&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;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Draw the square&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This comparison illustrates the fundamental trade-off: Canvas 2D is designed for simplicity and requires minimal code for basic operations, while WebGL 2 demands more setup but gives you direct control over the GPU's rendering pipeline. This extra complexity enables the advanced 3D graphics and performance that WebGL 2 is designed for.&lt;/p&gt;

&lt;p&gt;By leveraging GPU acceleration, WebGL 2 can render complex 3D scenes at 60fps or higher, enabling applications like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Interactive 3D data visualizations&lt;/li&gt;
&lt;li&gt;  Browser-based games with sophisticated graphics&lt;/li&gt;
&lt;li&gt;  Architectural and product visualization tools&lt;/li&gt;
&lt;li&gt;  Physics simulations and particle systems&lt;/li&gt;
&lt;li&gt;  Image processing and filters with real-time performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The cost? A steeper learning curve compared to other web technologies, but the payoff is tremendous visual power that was previously only available in native applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebGL 2 as a State Machine
&lt;/h2&gt;

&lt;p&gt;One of the first concepts needed to grasp is understanding that WebGL 2 functions as a state machine. This means its operations depend on previously set states rather than exclusively on parameters passed to functions - a fundamental characteristic that makes WebGL 2 different from typical JavaScript patterns.&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.amazonaws.com%2Fuploads%2Farticles%2Fvsdojf7m2qqk4icenubb.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.amazonaws.com%2Fuploads%2Farticles%2Fvsdojf7m2qqk4icenubb.png" alt="WebGL 2 State Machine Diagram" width="800" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In most JavaScript code, a function's behavior is determined primarily by the parameters you pass in:&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;// Regular JavaScript: behavior determined by parameters&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculateSomething&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;paramA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paramB&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WebGL 2, however, works differently:&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;// Set a state&lt;/span&gt;
&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useProgram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shaderProgram&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniform1f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uniformLocation&lt;/span&gt;&lt;span class="p"&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="c1"&gt;// This function's behavior depends on the states set above&lt;/span&gt;
&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawArrays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TRIANGLES&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;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;code&gt;drawArrays()&lt;/code&gt; will use whatever shader program and uniform values were previously set. Understanding this state-based architecture is crucial for effective WebGL 2 programming.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Programmable Pipeline
&lt;/h2&gt;

&lt;p&gt;A fundamental aspect of WebGL 2 is its enhanced programmable pipeline, implemented through shader programs.&lt;/p&gt;

&lt;p&gt;There are two mandatory shader programs that you need to write:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Vertex Shader&lt;/strong&gt;: position vertices&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Fragment Shader&lt;/strong&gt;: color pixels&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The programmable pipeline represents a fundamental shift from fixed-function graphics rendering to a flexible, developer-controlled system where you can write custom programs that execute directly on the GPU.&lt;/p&gt;

&lt;p&gt;Before programmable shaders, graphics hardware used a "fixed-function pipeline" with built-in, unchangeable algorithms for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Vertex transformation (multiply by matrices)&lt;/li&gt;
&lt;li&gt;  Lighting (typically Phong or Blinn-Phong model)&lt;/li&gt;
&lt;li&gt;  Texture mapping (basic sampling only)&lt;/li&gt;
&lt;li&gt;  Fog calculations&lt;/li&gt;
&lt;li&gt;  Color blending&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The modern programmable pipeline mirrors the physical hardware architecture of GPUs and enables efficient parallel execution of custom rendering code, giving developers unprecedented control over visual output. We'll explore how to write these shader programs in detail in the next article.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebGL 2 Coordinate System
&lt;/h2&gt;

&lt;p&gt;Another important concept is the WebGL 2 coordinate system, which differs fundamentally from what web developers are used to in CSS.&lt;/p&gt;

&lt;p&gt;In WebGL 2:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The origin (0,0) is located at the center of the canvas, not the top-left corner&lt;/li&gt;
&lt;li&gt;  The Y-axis points upward (positive is up), not downward&lt;/li&gt;
&lt;li&gt;  All coordinates are normalized between -1.0 and 1.0 regardless of canvas size&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.amazonaws.com%2Fuploads%2Farticles%2Fa1f5arcy1w8yd6ik2z0u.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.amazonaws.com%2Fuploads%2Farticles%2Fa1f5arcy1w8yd6ik2z0u.png" alt="WebGL Coordinate System showing the normalized device coordinates with the origin (0,0) at center" width="800" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Understanding this coordinate system from the beginning helps prevent common positioning and orientation errors in your WebGL 2 projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;This post covered the WebGL 2 fundamentals. In upcoming articles, I'll deep dive into: shader techniques, performance optimization strategies, building a practical fluid simulation.&lt;/p&gt;

&lt;p&gt;If you're working on graphics-heavy applications or need to squeeze more performance out of browser-based visualizations, WebGL 2's capabilities are worth the steeper learning curve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next article&lt;/strong&gt;: &lt;a href="https://dev.to/olha-stefanishyna/webgl-2-basics-drawing-a-full-screen-quad-3fcd"&gt;WebGL 2 Basics: Drawing a Full-Screen Quad&lt;/a&gt; - Learn GLSL ES 3.0 and implement your first WebGL 2 program.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webgl</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Rate Limiting: Architectural Layers in Web Applications</title>
      <dc:creator>Olha Stefanishyna</dc:creator>
      <pubDate>Fri, 23 May 2025 14:14:31 +0000</pubDate>
      <link>https://dev.to/olha-stefanishyna/rate-limiting-architectural-layers-in-web-applications-3g76</link>
      <guid>https://dev.to/olha-stefanishyna/rate-limiting-architectural-layers-in-web-applications-3g76</guid>
      <description>&lt;h2&gt;
  
  
  Introduction&lt;a id="intro"&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Rate limiting is a critical defensive strategy for web applications, allowing you to control the flow of incoming and outgoing requests. By restricting how many requests a client can make in a given time window, you can maintain system stability, prevent abuse, and ensure fair resource allocation across your user base.&lt;/p&gt;

&lt;p&gt;This configuration demonstrates infrastructure-level rate limiting by setting request identifiers, allocating memory, defining burst-tolerant limits, and applying rules to specific URLs.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Critical Role of Backend Rate Limiting
&lt;/h3&gt;

&lt;p&gt;Effective backend rate limiting is foundational for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;System Stability&lt;/strong&gt;: Prevents resource exhaustion during traffic spikes&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Security Enhancement&lt;/strong&gt;: Mitigates brute force attacks, credential stuffing, and DDoS attempts&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Fair Resource Usage&lt;/strong&gt;: Ensures equitable access for all users&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Performance Optimization&lt;/strong&gt;: Improves overall latency for legitimate users by smoothing request bursts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Backend rate limiting can be implemented across three distinct architectural layers, each operating at different points in the request lifecycle. These layers differ in their proximity to the client, available context, and performance characteristics. Understanding where each layer fits in your application architecture is crucial for designing an effective rate limiting strategy that balances early traffic filtering with granular business logic control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Infrastructure Layer Rate Limiting &lt;a id="infrastructure-layer"&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The infrastructure layer represents your first line of defense. At this level, rate limiting is typically implemented in load balancers and reverse proxies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Early Rejection: Blocks excessive traffic before it consumes application server resources.&lt;/li&gt;
&lt;li&gt;  Global Scope: Can enforce limits across all underlying services.&lt;/li&gt;
&lt;li&gt;  Performance: Typically implemented in highly optimized native code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example with Nginx&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;limit_req_zone&lt;/span&gt; &lt;span class="nv"&gt;$binary_remote_addr&lt;/span&gt; &lt;span class="s"&gt;zone=mylimit:10m&lt;/span&gt; &lt;span class="s"&gt;rate=10r/s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/api/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;limit_req&lt;/span&gt; &lt;span class="s"&gt;zone=mylimit&lt;/span&gt; &lt;span class="s"&gt;burst=20&lt;/span&gt; &lt;span class="s"&gt;nodelay&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://my_upstream&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;This configuration illustrates how infrastructure-level rate limiting works by defining request identifiers, allocating tracking memory, setting rate limits with burst tolerance, and applying these rules to specific URL patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations and Considerations
&lt;/h3&gt;

&lt;p&gt;While infrastructure-level rate limiting is powerful, it has limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Typically relies on IP-based identification, which can penalize multiple users behind a NAT or corporate proxy.&lt;/li&gt;
&lt;li&gt;  CDN Interaction: If behind a CDN, ensure Nginx sees the actual client IP via headers like X-Forwarded-For.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more granular control, consider using different identifiers by setting custom keys like API tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Use API key from header instead of IP&lt;/span&gt;
&lt;span class="k"&gt;limit_req_zone&lt;/span&gt; &lt;span class="nv"&gt;$http_x_api_key&lt;/span&gt; &lt;span class="s"&gt;zone=api_limits:10m&lt;/span&gt; &lt;span class="s"&gt;rate=5r/s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Application Gateway Layer Rate Limiting &lt;a id="gateway-layer"&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This layer exists closer to the application, often within the application framework itself. In Next.js applications, this is often implemented using Middleware, which intercepts requests before they reach your route handlers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example: Next.js Middleware with in-memory rate limiter (for illustration only)&lt;/strong&gt;&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;// middleware.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/server&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;requestStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** In-memory request tracking **/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;middleware&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="k"&gt;if &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="nx"&gt;nextUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/protected-route&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** Extract client IP **/&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;isRateLimited&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** Check if user exceeded rate limit **/&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="nx"&gt;isRateLimited&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Too many requests.&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="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="cm"&gt;/** Standard rate limit headers **/&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="cm"&gt;/** Update request tracking **/&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;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;matcher&lt;/span&gt;&lt;span class="p"&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;/api/:path*&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;h4&gt;
  
  
  Key Advantages of Middleware Rate Limiting
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Edge Execution&lt;/strong&gt;: Next.js Middleware can run at the edge, providing low latency decisions&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Dynamic Rules&lt;/strong&gt;: Easier to implement rules based on application state or user roles.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Contextual Awareness&lt;/strong&gt;: Access to request headers, cookies, JWTs for finer-grained identifier choice.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Considerations
&lt;/h4&gt;

&lt;p&gt;The in-memory approach shown here is purely illustrative. In any production environment, you should use a persistent, distributed store like Redis, or a specialized rate limiting service. In-memory solutions are unreliable because they lose state during restarts, deployments, or crashes, and can accumulate memory leaks over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Application Service Layer Rate Limiting &lt;a id="service-layer"&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This involves implementing rate limits directly within the application's business logic, typically at the controller, service, or individual function level.&lt;br&gt;
In Next.js rate limiting at the service layer is implemented directly in your API route handlers or server-side logic. This offers fine-grained control with the ability to integrate business rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example - Next.js API Route with a simple in-memory store (Illustrative):&lt;/strong&gt;&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;// pages/api/service-data.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** Initialize request tracking **/&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;MAX_REQUESTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** Configure rate limit **/&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;WINDOW_MS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** Define time window **/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&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;identifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** Extract appropriate identifier (API key, user ID, or IP) **/&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;recentRequests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** Get user's requests within current time window **/&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="nx"&gt;recentRequests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;MAX_REQUESTS&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&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;Too many requests. Please try again later.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;retryAfter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="cm"&gt;/** Calculate retry time **/&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/** Update request tracking with current request **/&lt;/span&gt;
    &lt;span class="cm"&gt;/** Clean up old entries to prevent memory leaks **/&lt;/span&gt;

    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="cm"&gt;/** Return requested data **/&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;h4&gt;
  
  
  Considerations
&lt;/h4&gt;

&lt;p&gt;Service layer rate limiting may affect performance, so this approach should be reserved for scenarios where middleware solutions aren't sufficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Standard Headers for Rate Limiting
&lt;/h2&gt;

&lt;p&gt;Consistent client communication is key. Always include these HTTP headers in responses from rate-limited endpoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;X-RateLimit-Limit&lt;/code&gt;: Maximum requests allowed in the current window&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;X-RateLimit-Remaining&lt;/code&gt;: Number of requests left in the current window&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;X-RateLimit-Reset&lt;/code&gt;: Unix timestamp or seconds until the limit resets&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;Retry-After&lt;/code&gt;: Seconds to wait before making another request (when rate limited)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that the IETF has a draft specification for standardized rate limiting headers, introducing RateLimit and RateLimit-Policy to consolidate rate limiting information. However, many APIs still use legacy headers with the X- prefix, such as X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Backend Rate Limiting&lt;a id="#best-practices"&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  Layered Approach: Combine infrastructure, gateway, and application-level limits for comprehensive protection.&lt;/li&gt;
&lt;li&gt;  Choose Appropriate Identifiers: Use IP for broad, unauthenticated traffic; API keys or user IDs for authenticated traffic.&lt;/li&gt;
&lt;li&gt;  Failure Handling: Decide whether to fail open (allow requests) or fail closed (block requests) when rate limiting services are unavailable.&lt;/li&gt;
&lt;li&gt;  Adaptive Limits: Consider implementing dynamic limits that adjust based on system health, user tiers, or traffic patterns.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Backend rate limiting is a multi-layered defense mechanism that protects your application while ensuring fair resource allocation. By implementing rate limiting at the infrastructure, gateway, and service layers, you create a robust system that can handle varying traffic patterns while preventing abuse.&lt;/p&gt;

&lt;p&gt;Remember that well-implemented rate limiting should be invisible to normal users while protecting your system from abuse. When done right, it's an essential component of a resilient, scalable backend architecture.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>nextjs</category>
      <category>softwareengineering</category>
      <category>ratelimiting</category>
    </item>
  </channel>
</rss>
