<?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: Amit Dudhat</title>
    <description>The latest articles on DEV Community by Amit Dudhat (@wpamitkumar).</description>
    <link>https://dev.to/wpamitkumar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F204618%2Fa7cd241c-ead1-44b9-a86e-e05a6bb19971.jpg</url>
      <title>DEV Community: Amit Dudhat</title>
      <link>https://dev.to/wpamitkumar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wpamitkumar"/>
    <language>en</language>
    <item>
      <title>I built a prefetcher that knows when to stop</title>
      <dc:creator>Amit Dudhat</dc:creator>
      <pubDate>Tue, 29 Sep 2026 07:27:04 +0000</pubDate>
      <link>https://dev.to/wpamitkumar/i-built-a-prefetcher-that-knows-when-to-stop-3219</link>
      <guid>https://dev.to/wpamitkumar/i-built-a-prefetcher-that-knows-when-to-stop-3219</guid>
      <description>&lt;p&gt;A few years ago, Alexandre Dieulot released &lt;a href="https://instant.page/" rel="noopener noreferrer"&gt;instant.page&lt;/a&gt;. It was a fantastic piece of work. You dropped a 1KB script into your HTML, and page transitions felt instantaneous because the browser started fetching the HTML the second someone hovered over a link.&lt;/p&gt;

&lt;p&gt;It felt like free performance.&lt;/p&gt;

&lt;p&gt;After running naive prefetchers across a few production projects, though, you start noticing the flaws. Two problems in particular kept bugging me:&lt;/p&gt;

&lt;p&gt;First, &lt;strong&gt;waiting for &lt;code&gt;pointerover&lt;/code&gt; is usually too late&lt;/strong&gt;. On a fast, deliberate click, hover gives you very little runway before the mouse button fires, ignoring the entire flight path that came before it.&lt;/p&gt;

&lt;p&gt;Second, and this is the bigger issue, &lt;strong&gt;most prefetchers are recklessly optimistic&lt;/strong&gt;. They assume the client has great bandwidth, an unmetered connection, plenty of battery, and that your origin server is healthy. If your database connection pool maxes out or an API route starts throwing &lt;code&gt;503 Service Unavailable&lt;/code&gt; or &lt;code&gt;429 Too Many Requests&lt;/code&gt;, a naive prefetcher does not care. Hundreds of concurrent visitors moving their mice around will keep pelting your struggling backend with speculative requests, turning a small traffic hiccup into a cascading outage.&lt;/p&gt;

&lt;p&gt;I wanted something smarter: a library that could catch user intent before the cursor arrives, with enough self-awareness to back off the moment resources get tight.&lt;/p&gt;

&lt;p&gt;That is why I built &lt;strong&gt;&lt;a href="https://github.com/FlashPageJS/FlashPage" rel="noopener noreferrer"&gt;Flash Page&lt;/a&gt;&lt;/strong&gt;. It is a zero-dependency, vanilla JS library (around 8KB gzipped) that rethinks how speculative navigation should work.&lt;/p&gt;




&lt;h2&gt;
  
  
  Predicting the click before the hover
&lt;/h2&gt;

&lt;p&gt;If you watch how people use mice and trackpads, they do not teleport. When someone decides to click a link, their hand accelerates across the screen along a relatively straight vector toward that target. Waiting for the cursor to touch the anchor means ignoring that entire trajectory.&lt;/p&gt;

&lt;p&gt;In Flash Page, instead of only listening for hovers, I set up a pointer tracker throttled to around 40Hz (sampling every ~25ms). When the cursor moves, it calculates the velocity vector (&lt;code&gt;vx, vy&lt;/code&gt;) and checks if the movement represents deliberate momentum rather than idle drift or a wild gesture across the screen.&lt;/p&gt;

&lt;p&gt;If the cursor is moving deliberately, Flash Page casts a 3-point proximity cone forward along the heading with a 120ms lookahead horizon. Since &lt;code&gt;vx&lt;/code&gt; and &lt;code&gt;vy&lt;/code&gt; are in pixels per millisecond, multiplying by 120 projects the cursor position roughly 120 milliseconds into the future:&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;// Sampling the pointer vector at ~40Hz&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;lastX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;dt&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;vy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;lastY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;dt&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;speedSq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;vx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;vy&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;vy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Skip slow drift (&amp;lt; 0.3 px/ms) or chaotic flicks (&amp;gt; 3.9 px/ms)&lt;/span&gt;
&lt;span class="c1"&gt;// speedSq is in px²/ms², so 0.1 to 15 corresponds to roughly 0.3 to 3.9 px/ms&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;speedSq&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;speedSq&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;15&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;projDist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 120ms temporal lookahead horizon (vx is px/ms)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;speedSq&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Normal vector to fan out a cone&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;vy&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;len&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;ny&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="nx"&gt;vx&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;len&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;spread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&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;testPoints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;vx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;projDist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;vy&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;projDist&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;vx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;projDist&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;nx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;spread&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;vy&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;projDist&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ny&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;spread&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;vx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;projDist&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;nx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;spread&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;vy&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;projDist&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;ny&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;spread&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pt&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;testPoints&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;el&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;elementFromPoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;closest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a[href]&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="nx"&gt;anchor&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;isPreloadable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;low&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By testing three points (one directly along the 120ms projected path and two slightly flared out to the sides), we catch links that the user is heading toward before the cursor arrives. With a 120ms lookahead horizon, the theoretical lead time is up to that full 120ms window before the cursor even touches the link.&lt;/p&gt;

&lt;p&gt;If someone is just casually drifting their mouse over text or resting their hand, the speed threshold ignores it and falls back to a clean 65ms hover debounce.&lt;/p&gt;




&lt;h2&gt;
  
  
  What about mobile? (A tiny client-side Markov model)
&lt;/h2&gt;

&lt;p&gt;Mouse velocity works well on desktop. On a phone or tablet, however, there is no cursor to track. The first event you get is &lt;code&gt;pointerdown&lt;/code&gt;, which gives you virtually zero runway before navigation.&lt;/p&gt;

&lt;p&gt;To help with this, Flash Page keeps track of navigation sequences locally using a tiny Markov chain.&lt;/p&gt;

&lt;p&gt;Most websites have predictable user flows. If someone is reading &lt;code&gt;/docs/installation&lt;/code&gt;, there is a high probability their next stop is &lt;code&gt;/docs/quickstart&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Whenever a user navigates between pages on your site, Flash Page updates a transition frequency table in the browser's &lt;code&gt;localStorage&lt;/code&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;// On route changes:&lt;/span&gt;
&lt;span class="nf"&gt;recordTransition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;previousPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// During idle time on the current page:&lt;/span&gt;
&lt;span class="nf"&gt;requestIdleCallback&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;predictNextLink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;preloadMatchingLink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextPath&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;Two design choices here were non-negotiable:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Privacy and zero tracking:&lt;/strong&gt; This transition table lives strictly in the user's &lt;code&gt;localStorage&lt;/code&gt; as a small JSON map, capped at 50 source paths (a few KB at most). It never sends a single byte to an external server. A performance utility has no business acting as an analytics tracker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conservative heuristics:&lt;/strong&gt; To avoid wasting mobile data, Flash Page will not speculate on a hunch. It only triggers a prefetch if a specific path transition has occurred at least 3 times on that device and has at least a 60% probability.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One caveat for WebKit: Safari's storage policies can clear script-written &lt;code&gt;localStorage&lt;/code&gt; after a period of user inactivity, which means transition counts may periodically reset for infrequent visitors.&lt;/p&gt;




&lt;h2&gt;
  
  
  Using native Speculation Rules instead of fighting the browser
&lt;/h2&gt;

&lt;p&gt;Historically, prefetching meant either appending &lt;code&gt;&amp;lt;link rel="prefetch" as="document" href="..."&amp;gt;&lt;/code&gt; into the DOM or firing off a background &lt;code&gt;fetch()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Both approaches have quirks. &lt;code&gt;fetch()&lt;/code&gt; leaves you with the problem of managing response caches and does not prepare subresources. &lt;code&gt;&amp;lt;link rel="prefetch"&amp;gt;&lt;/code&gt; is treated inconsistently across browsers (WebKit has historically not supported document prefetching by default).&lt;/p&gt;

&lt;p&gt;Chrome introduced early versions of list-based Speculation Rules (&lt;code&gt;source: "list"&lt;/code&gt;) in 2022-2023. What arrived in &lt;strong&gt;Chromium 121&lt;/strong&gt;, though, was the addition of &lt;strong&gt;Document Rules&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of requiring JavaScript to manually parse anchor tags and inject individual URLs into a list, you can inject declarative document rules. The browser's native C++ engine automatically monitors matching links in the DOM and handles the speculation out-of-process on a background network thread:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"speculationrules"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prefetch&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;source&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;document&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;where&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;and&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;href_matches&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/*&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;not&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;selector_matches&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[data-no-flash]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[download]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[rel~='nofollow']&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[href*='/logout']&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="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eagerness&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;moderate&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a user clicks a prefetched link, network latency for the HTML document itself is already eliminated (TTFB drops down to near-zero cache-hit speeds). If you configure &lt;code&gt;specrules: 'prerender'&lt;/code&gt;, Chromium renders the entire target page in a background process, making the visible transition practically instantaneous.&lt;/p&gt;

&lt;p&gt;A subtle point about eagerness: in the rule above, Chromium's native &lt;code&gt;moderate&lt;/code&gt; eagerness on desktop triggers when a user hovers over a link for ~200ms or presses down. What native rules cannot do on their own is project vector trajectory cones before the hover happens, or respect dynamic server backpressure, which is where Flash Page steps in.&lt;/p&gt;

&lt;p&gt;Because browser support is not uniform across the web, Flash Page detects engine capabilities and chooses the cleanest available strategy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chromium 121+&lt;/strong&gt;: Injects declarative Document Rules (with fallback to targeted speculation-list injection).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firefox and older Chrome&lt;/strong&gt;: Injects &lt;code&gt;&amp;lt;link rel="prefetch" as="document"&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safari / WebKit&lt;/strong&gt;: Since Safari has historically not supported document prefetch tags by default, Flash Page falls back to an internal cache-warming &lt;code&gt;fetch()&lt;/code&gt; request with &lt;code&gt;{ priority: 'low' }&lt;/code&gt; so that HTML and critical headers get seeded into the HTTP cache. As with any HTTP prefetch, warming the browser cache only helps if the response is cacheable; pages served with &lt;code&gt;Cache-Control: no-store&lt;/code&gt; or &lt;code&gt;private&lt;/code&gt; will naturally not benefit from background fetching.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Backpressure: Knowing when to stop
&lt;/h2&gt;

&lt;p&gt;This was the main reason I built Flash Page in the first place.&lt;/p&gt;

&lt;p&gt;Prefetched requests are non-essential by definition. If your server is healthy, prefetching makes your product feel great. But if your origin is groaning under heavy load, speculative requests are pure waste.&lt;/p&gt;

&lt;p&gt;If a server responds to a &lt;code&gt;fetch()&lt;/code&gt;-based speculative request with an HTTP &lt;code&gt;429 Too Many Requests&lt;/code&gt; or &lt;code&gt;503 Service Unavailable&lt;/code&gt;, Flash Page immediately inspects the response and pulls the emergency brake:&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="k"&gt;if &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="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;503&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;retryHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Retry-After&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;waitSeconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// sensible fallback&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;retryHeader&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;asSeconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;retryHeader&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;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isFinite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;asSeconds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;asSeconds&lt;/span&gt;
      &lt;span class="p"&gt;:&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;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;retryHeader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&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="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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isFinite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;waitSeconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt; &lt;span class="c1"&gt;// cap at 5 min&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;setBackpressure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;waitSeconds&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;When backpressure is tripped, all JavaScript-directed operations stand down: no vector lookaheads, no hover timers, and no idle Markov predictions. If your API says "back off for 60 seconds", Flash Page suspends all dynamic prefetch queues until the window clears. Note that if a declarative &lt;code&gt;&amp;lt;script type="speculationrules"&amp;gt;&lt;/code&gt; tag has already been injected into the DOM on Chromium, the browser engine continues processing those document rules until navigation; the client-side backpressure pause governs all dynamic JavaScript queues and subsequent speculative requests.&lt;/p&gt;

&lt;p&gt;A technical detail on how this applies across different engines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On the &lt;code&gt;fetch()&lt;/code&gt; path (Safari cache warming and manual preloads), Flash Page inspects responses directly and triggers backpressure automatically.&lt;/li&gt;
&lt;li&gt;On Chromium's native Speculation Rules path, the browser makes requests out of process, meaning client JavaScript cannot read a 503 response. Here, the defense is handled on the server or CDN layer: reverse proxies can inspect incoming headers and return a 503 or drop speculative requests when origin load spikes. The browser silently discards that failed speculation, and real user clicks still work normally.&lt;/li&gt;
&lt;li&gt;On the client side, if your application monitoring, telemetry, or service worker detects degraded backend health, you can also trigger backpressure programmatically across all client-directed operations by calling &lt;code&gt;setBackpressure(seconds)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, because browsers forbid client JavaScript from setting &lt;code&gt;Sec-*&lt;/code&gt; headers, Flash Page tags its same-origin fallback fetches with &lt;code&gt;Purpose: prefetch&lt;/code&gt;. Meanwhile, Chromium's native engine automatically attaches &lt;code&gt;Sec-Purpose: prefetch&lt;/code&gt; to its out-of-process requests. Both headers give CDNs and reverse proxies an immediate signal to shed speculative traffic at the edge before it ever reaches your origin.&lt;/p&gt;

&lt;p&gt;Before doing any speculative work, it also checks the device environment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connection quality:&lt;/strong&gt; If &lt;code&gt;navigator.connection.saveData&lt;/code&gt; is turned on, or if the user is on a slow 2G connection, prefetching is turned off entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Battery state:&lt;/strong&gt; If the device is discharging and battery drops below 20%, it pauses to conserve power.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low memory:&lt;/strong&gt; If &lt;code&gt;navigator.deviceMemory&lt;/code&gt; reports 1GB or less, it steps aside.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency cap:&lt;/strong&gt; It limits simultaneous inflight preloads to 3 requests, using low-priority fetches (&lt;code&gt;priority: 'low'&lt;/code&gt;) to minimize bandwidth contention with current-page images or API calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep in mind that &lt;code&gt;navigator.connection&lt;/code&gt;, &lt;code&gt;getBattery()&lt;/code&gt;, and &lt;code&gt;deviceMemory&lt;/code&gt; are primarily Chromium APIs. Where available, Flash Page uses them; on Safari and Firefox, where these APIs are not exposed, those hardware checks are safely skipped and prefetching continues normally.&lt;/p&gt;




&lt;h2&gt;
  
  
  Not breaking applications
&lt;/h2&gt;

&lt;p&gt;Prefetching can cause real headaches if you do not account for state-changing links.&lt;/p&gt;

&lt;p&gt;Flash Page comes with sensible guards out of the box:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It automatically skips destructive URLs (&lt;code&gt;/logout&lt;/code&gt;, &lt;code&gt;/signout&lt;/code&gt;, &lt;code&gt;/delete&lt;/code&gt;, &lt;code&gt;/destroy&lt;/code&gt;, &lt;code&gt;/remove&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;It skips links with framework action attributes like Rails or Turbo's &lt;code&gt;[data-method]&lt;/code&gt;, &lt;code&gt;[data-turbo-method]&lt;/code&gt;, or HTMX's &lt;code&gt;[hx-post]&lt;/code&gt; and &lt;code&gt;[hx-delete]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It ignores links with &lt;code&gt;target="_blank"&lt;/code&gt;, &lt;code&gt;download&lt;/code&gt;, or &lt;code&gt;rel="nofollow"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It uses a &lt;code&gt;MutationObserver&lt;/code&gt; under the hood so newly mounted links in SPAs (React, Vue, Svelte, Turbo streams) are automatically monitored without having to call an update method.&lt;/li&gt;
&lt;li&gt;Heads-up on prerendering: If you choose to enable full prerendering (&lt;code&gt;specrules: 'prerender'&lt;/code&gt;), be aware that the browser executes page JavaScript in a hidden prerender context (not a visible tab). If your application records analytics pageviews or auto-plays media immediately on script execution, scripts should check &lt;code&gt;document.prerendering&lt;/code&gt; or listen for the &lt;code&gt;prerenderingchange&lt;/code&gt; event so those actions only fire once the user actually views the page.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The DevTools HUD (&lt;code&gt;Ctrl + Shift + F&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;When you build something that makes decisions based on cursor vectors and probability tables, you need a way to inspect what it is doing in real time without digging through console logs.&lt;/p&gt;

&lt;p&gt;Pressing &lt;code&gt;Ctrl + Shift + F&lt;/code&gt; (or &lt;code&gt;Cmd + Shift + F&lt;/code&gt; on Mac) toggles a tiny diagnostic HUD right on the page. If that key combination collides with a browser extension or dev tool in your environment, you can also trigger the overlay programmatically by calling &lt;code&gt;toggleHUD()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Inside the HUD:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shows which engine is active (&lt;code&gt;document-rules&lt;/code&gt;, &lt;code&gt;speculation-list&lt;/code&gt;, &lt;code&gt;link&lt;/code&gt;, or &lt;code&gt;fetch&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Displays live queue depth and active requests.&lt;/li&gt;
&lt;li&gt;Shows whether server backpressure is currently active (and how many seconds remain).&lt;/li&gt;
&lt;li&gt;Highlights speculated links on the page with a subtle cyan outline, so you can watch your cursor trajectory trigger preloads as you move across the screen.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;p&gt;Flash Page is distributed as pure vanilla JS with zero dependencies in two builds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full bundle (~8KB gzipped):&lt;/strong&gt; Vector trajectory, Markov idle predictions, DevTools HUD, and critical CSS/font subresource pre-warming.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lite bundle (~5.7KB gzipped):&lt;/strong&gt; Stripped down to essential prefetching for clean blogs and documentation sites.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you already use &lt;code&gt;instant.page&lt;/code&gt;, Flash Page is designed to be backward-compatible, honoring existing &lt;code&gt;data-instant&lt;/code&gt; and &lt;code&gt;data-no-instant&lt;/code&gt; markup alongside newer &lt;code&gt;data-flash&lt;/code&gt; directives.&lt;/p&gt;

&lt;p&gt;You can drop it in via CDN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/flash-page@1.0.1/flashpage.min.js"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or host locally&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/flashpage.min.js"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or install it through NPM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;flash-page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;init&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;flash-page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;intensity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;predictive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Enables cursor trajectory vectoring&lt;/span&gt;
  &lt;span class="na"&gt;specrules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prefetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// 'prefetch' | 'prerender' | 'no'&lt;/span&gt;
  &lt;span class="na"&gt;markov&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;// Enables idle-time Markov navigation prediction&lt;/span&gt;
  &lt;span class="na"&gt;subresources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// Pre-warms render-blocking CSS &amp;amp; web fonts&lt;/span&gt;
  &lt;span class="na"&gt;backpressure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;       &lt;span class="c1"&gt;// Honors 429/503 Retry-After headers&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;This is an active project, and I want to be honest about its limits. Mouse trajectory on desktop feels great in practice, but predicting intent on touch devices will always be constrained by the lack of pre-touch input.&lt;/p&gt;

&lt;p&gt;The goal with Flash Page is not to download the entire internet before someone clicks. It is to make a thoughtful, well-timed guess when someone is clearly heading for a link, and to keep that guess from costing the user bandwidth or adding load to a server that is already struggling.&lt;/p&gt;

&lt;p&gt;If you maintain high-traffic backends or work on frontend performance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How does your CDN cache layer handle speculative document traffic?&lt;/li&gt;
&lt;li&gt;What concerns would you have before turning something like this on?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code is fully open source:&lt;br&gt;&lt;br&gt;
👉 &lt;strong&gt;&lt;a href="https://github.com/FlashPageJS/FlashPage" rel="noopener noreferrer"&gt;GitHub: FlashPageJS/FlashPage&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webperf</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
