<?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: Mohammad</title>
    <description>The latest articles on DEV Community by Mohammad (@mvahedii).</description>
    <link>https://dev.to/mvahedii</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%2F189411%2Fd53e2f42-e867-48a1-b8e5-5ef46b148688.jpeg</url>
      <title>DEV Community: Mohammad</title>
      <link>https://dev.to/mvahedii</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mvahedii"/>
    <language>en</language>
    <item>
      <title>Service Workers Deep Dive: What Actually Happens in the Browser</title>
      <dc:creator>Mohammad</dc:creator>
      <pubDate>Fri, 19 Dec 2025 10:13:48 +0000</pubDate>
      <link>https://dev.to/mvahedii/service-workers-deep-dive-what-actually-happens-in-the-browser-e1l</link>
      <guid>https://dev.to/mvahedii/service-workers-deep-dive-what-actually-happens-in-the-browser-e1l</guid>
      <description>&lt;h2&gt;
  
  
  What Is a Service Worker?
&lt;/h2&gt;

&lt;p&gt;A Service Worker is a JavaScript file that runs in the background, separate from the main page.&lt;/p&gt;

&lt;p&gt;Key characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runs on a different thread than the UI&lt;/li&gt;
&lt;li&gt;Has no access to the DOM or &lt;code&gt;window&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Uses only asynchronous APIs&lt;/li&gt;
&lt;li&gt;Can intercept and handle network requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Service Workers act as a programmable network proxy between your application and the internet. Because they run outside the page lifecycle, the browser can start, stop, or restart them whenever needed. For this reason, they must not depend on in-memory state.&lt;/p&gt;




&lt;h2&gt;
  
  
  Registering a Service Worker
&lt;/h2&gt;

&lt;p&gt;To use a Service Worker, you register it from your page:&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="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/sw.js&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;When this runs, the browser:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fetches the &lt;code&gt;sw.js&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Compares its &lt;strong&gt;content&lt;/strong&gt; with the previously stored version&lt;/li&gt;
&lt;li&gt;Starts the lifecycle if the file is new or changed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The comparison is content-based. Even a one-character change in the file triggers a new install. File names, comments, or version variables do not matter unless they change the actual bytes of the file.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Lifecycle: install → activate
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;install&lt;/code&gt; event is the first event fired for a new version of a Service Worker.&lt;/p&gt;

&lt;p&gt;Its main purpose is to prepare the application for offline usage by caching required assets.&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="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;install&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app-shell-v1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addAll&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/styles.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/app.js&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;event.waitUntil()&lt;/code&gt; tells the browser not to finish installation until the promise resolves. If this promise rejects, the Service Worker is discarded and never reaches the activate phase. This makes &lt;code&gt;install&lt;/code&gt; an atomic step: it either fully succeeds or fails completely.&lt;/p&gt;




&lt;h3&gt;
  
  
  Activate
&lt;/h3&gt;

&lt;p&gt;After installation, the Service Worker moves to the &lt;code&gt;activate&lt;/code&gt; phase.&lt;/p&gt;

&lt;p&gt;This event is typically used to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remove old cache versions&lt;/li&gt;
&lt;li&gt;Release unused resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only one Service Worker version can be active at a time. If an older version is controlling open pages, the new one waits. This prevents a page from changing its controlling logic while it is running.&lt;/p&gt;




&lt;h2&gt;
  
  
  Page Control and Reloading
&lt;/h2&gt;

&lt;p&gt;A page decides whether it is controlled by a Service Worker &lt;strong&gt;at load time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The page that calls &lt;code&gt;register()&lt;/code&gt; is not controlled&lt;/li&gt;
&lt;li&gt;A reload or new tab is required for control to take effect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One Service Worker can control many pages at once. All controlled pages share the same worker instance. Global variables are shared and unreliable because the worker can be terminated and restarted at any time. Persistent data must be stored in Cache API or IndexedDB.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cache API (Detailed)
&lt;/h2&gt;

&lt;p&gt;The Cache API is the primary persistent storage mechanism used by Service Workers to store network responses.&lt;/p&gt;

&lt;p&gt;At its core, the Cache API stores &lt;strong&gt;Response objects&lt;/strong&gt;, each associated with a &lt;strong&gt;Request object&lt;/strong&gt; as its key. This means the cache behaves like a map:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request  →  Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What exactly is stored?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Only &lt;strong&gt;Response&lt;/strong&gt; objects&lt;/li&gt;
&lt;li&gt;Metadata such as headers, status code, and body stream&lt;/li&gt;
&lt;li&gt;No automatic serialization of custom data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Cache API does &lt;strong&gt;not&lt;/strong&gt; store raw files or URLs. It stores full HTTP responses, exactly as they were returned by the network.&lt;/p&gt;




&lt;h3&gt;
  
  
  How matching works
&lt;/h3&gt;

&lt;p&gt;When you query the cache, the browser tries to find a matching entry based on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request URL&lt;/li&gt;
&lt;li&gt;HTTP method (usually GET)&lt;/li&gt;
&lt;li&gt;Vary-related headers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matching logic closely follows standard HTTP caching rules, but it is fully controlled by your code. Nothing is automatically added or removed unless you explicitly do so.&lt;/p&gt;




&lt;h3&gt;
  
  
  Cache lifetime and persistence
&lt;/h3&gt;

&lt;p&gt;Cache entries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do &lt;strong&gt;not expire automatically&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Are &lt;strong&gt;not evicted&lt;/strong&gt; based on time or size&lt;/li&gt;
&lt;li&gt;Remain stored until explicitly deleted by your code or the user clears site data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of this, cache cleanup is a responsibility of the Service Worker, usually handled during the &lt;code&gt;activate&lt;/code&gt; phase.&lt;/p&gt;




&lt;h3&gt;
  
  
  Scope and isolation
&lt;/h3&gt;

&lt;p&gt;Cache API storage is scoped to the &lt;strong&gt;origin&lt;/strong&gt;, defined as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protocol + domain + port
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each domain has its own isolated cache storage&lt;/li&gt;
&lt;li&gt;Subdomains do not share caches&lt;/li&gt;
&lt;li&gt;Other websites cannot read or modify your cache&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isolation is critical for security and data integrity.&lt;/p&gt;




&lt;h3&gt;
  
  
  Debugging and inspection
&lt;/h3&gt;

&lt;p&gt;Cache contents can be inspected via browser DevTools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application → Cache Storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;View cached requests and responses&lt;/li&gt;
&lt;li&gt;Manually delete cache entries&lt;/li&gt;
&lt;li&gt;Simulate cache-related edge cases during development&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Service Workers run outside the page lifecycle and are event-driven&lt;/li&gt;
&lt;li&gt;Registration is required and update detection is based on file content&lt;/li&gt;
&lt;li&gt;The install phase prepares resources and must fully succeed&lt;/li&gt;
&lt;li&gt;Only one Service Worker version can be active at a time&lt;/li&gt;
&lt;li&gt;Page control is decided at load time, not dynamically&lt;/li&gt;
&lt;li&gt;Cache API stores full HTTP responses keyed by requests&lt;/li&gt;
&lt;li&gt;Cache data is persistent, origin-scoped, and fully controlled by code&lt;/li&gt;
&lt;li&gt;Proper cache management is essential for predictable PWA behavior&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>serviceworker</category>
      <category>pwa</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Anatomy of Webpack: What Really Happens Under the Hood</title>
      <dc:creator>Mohammad</dc:creator>
      <pubDate>Sat, 11 Oct 2025 07:32:57 +0000</pubDate>
      <link>https://dev.to/mvahedii/the-anatomy-of-webpack-what-really-happens-under-the-hood-2jee</link>
      <guid>https://dev.to/mvahedii/the-anatomy-of-webpack-what-really-happens-under-the-hood-2jee</guid>
      <description>&lt;p&gt;&lt;strong&gt;Webpack&lt;/strong&gt; is far more than a simple bundler—it’s a modular build pipeline. Under the hood, its execution can be divided into several deep technical phases that determine how source code becomes an optimized bundle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Resolution phase&lt;/strong&gt; comes first. Here, Webpack interprets every import or require statement and maps it to an actual file path. This process is powered by the enhanced-resolve package, which supports aliases, custom extensions, and module fallbacks. Multiple entry points can each trigger independent dependency graphs that Webpack merges later, deduplicating shared modules across entries.&lt;/p&gt;

&lt;p&gt;Once files are resolved, &lt;strong&gt;the Evaluation phase&lt;/strong&gt; begins. Each file runs through a chain of loaders—executed from right to left—to transform the content into JavaScript modules. For example, a .scss file may pass through sass-loader, css-loader, and style-loader, turning styles into JS-compatible strings. During this step, plugins can intercept runtime events and alter the output or even extract assets, as seen in MiniCssExtractPlugin.&lt;/p&gt;

&lt;p&gt;Next, &lt;strong&gt;the Optimization and Code Splitting stage&lt;/strong&gt; groups modules into chunks using SplitChunksPlugin, isolates vendor dependencies, and optionally separates the Webpack runtime—a small script that manages module loading in the browser. Dynamic import() statements create asynchronous chunks that the runtime loads only when needed, improving initial load time.&lt;/p&gt;

&lt;p&gt;Finally, &lt;strong&gt;Webpack’s Output phase&lt;/strong&gt; writes everything to disk, generating hashed filenames and a manifest that maps chunk identifiers to their physical files. The runtime uses this manifest at execution time to locate and load modules dynamically.&lt;/p&gt;

&lt;p&gt;In essence, Webpack is not just bundling—it’s constructing, transforming, optimizing, and orchestrating the runtime behavior of modern web applications.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>architecture</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Behind the Scenes of Performance: How We Built a Data-Driven Frontend Dashboard</title>
      <dc:creator>Mohammad</dc:creator>
      <pubDate>Tue, 23 Sep 2025 14:03:59 +0000</pubDate>
      <link>https://dev.to/mvahedii/behind-the-scenes-of-performance-how-we-built-a-data-driven-frontend-dashboard-1o5</link>
      <guid>https://dev.to/mvahedii/behind-the-scenes-of-performance-how-we-built-a-data-driven-frontend-dashboard-1o5</guid>
      <description>&lt;p&gt;Over the past few weeks, I’ve been diving deep into &lt;strong&gt;real-time performance monitoring&lt;/strong&gt; for our platform. It started with analyzing &lt;strong&gt;Round Trip Time (RTT)&lt;/strong&gt; — the total time for a request to leave the client, hit our servers, and return with a response. Monitoring RTT gave us the first insight into &lt;strong&gt;end-to-end latency&lt;/strong&gt;, helping us identify whether delays came from the client, network, or backend.&lt;/p&gt;

&lt;p&gt;Next, we built a &lt;strong&gt;custom query&lt;/strong&gt; to track &lt;strong&gt;Timeout and Cancelled Transactions&lt;/strong&gt;. The idea was simple: some transactions never finish properly, and without surfacing this metric, they remain hidden behind average response times. By separating these failed interactions, we could measure how often users &lt;strong&gt;abandon requests&lt;/strong&gt; or face &lt;strong&gt;slow services&lt;/strong&gt; — a critical signal for &lt;strong&gt;reliability&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We didn’t stop there. &lt;strong&gt;Rendering Time&lt;/strong&gt; became another key metric. Measuring &lt;strong&gt;frontend rendering duration&lt;/strong&gt; helped us spot &lt;strong&gt;performance bottlenecks&lt;/strong&gt; after the backend response arrives. It turns out, a fast API is not enough if the UI takes forever to paint meaningful content.&lt;/p&gt;

&lt;p&gt;We also added visibility into &lt;strong&gt;Failed Resource Loads&lt;/strong&gt; — things like &lt;strong&gt;missing scripts&lt;/strong&gt;, &lt;strong&gt;broken images&lt;/strong&gt;, or &lt;strong&gt;third-party calls&lt;/strong&gt; silently failing in the background. Tracking these issues revealed &lt;strong&gt;UX problems&lt;/strong&gt; that standard error metrics often miss.&lt;/p&gt;

&lt;p&gt;On top of this, we experimented with &lt;strong&gt;custom dashboards&lt;/strong&gt;, &lt;strong&gt;big number widgets&lt;/strong&gt; for KPIs, &lt;strong&gt;threshold-based alerts&lt;/strong&gt; in Sentry, and &lt;strong&gt;trend tables&lt;/strong&gt; for historical analysis. Each feature brought a new layer of &lt;strong&gt;observability&lt;/strong&gt;, from &lt;strong&gt;real-time incident detection&lt;/strong&gt; to &lt;strong&gt;long-term optimization&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What I loved about this journey is how each metric unlocked a different perspective: &lt;strong&gt;latency&lt;/strong&gt;, &lt;strong&gt;reliability&lt;/strong&gt;, &lt;strong&gt;frontend performance&lt;/strong&gt;, and &lt;strong&gt;user experience&lt;/strong&gt; — all connected. The result is a monitoring setup that’s not just &lt;strong&gt;reactive&lt;/strong&gt; but &lt;strong&gt;proactive&lt;/strong&gt; and &lt;strong&gt;actionable&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>sentry</category>
      <category>performance</category>
      <category>monitoring</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Why I’m Not Ready to Trust BiomeJS (Yet)</title>
      <dc:creator>Mohammad</dc:creator>
      <pubDate>Tue, 26 Aug 2025 13:45:15 +0000</pubDate>
      <link>https://dev.to/mvahedii/why-im-not-ready-to-trust-biomejs-yet-j9o</link>
      <guid>https://dev.to/mvahedii/why-im-not-ready-to-trust-biomejs-yet-j9o</guid>
      <description>&lt;p&gt;I tried BiomeJS recently and honestly—I couldn’t fall in love with it.&lt;/p&gt;

&lt;p&gt;Sure, it’s blazing fast, written in Rust, and promises to replace ESLint + Prettier in one shot. But the reality wasn’t that smooth:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It feels too opinionated. Instead of fitting into my workflow, it wanted me to adapt to its rules and defaults.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Speed is nice (I’ve seen people lint hundreds of files in milliseconds), but in my projects performance bottlenecks rarely come from linting. What matters more is flexibility and ecosystem support.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The maturity isn’t there yet. If I run into an edge case or need a plugin, Biome doesn’t give me the same safety net that a stable, well-adopted toolchain does.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Migration also isn’t as seamless as it sounds. Rewriting configs, figuring out which rules match what—I’d rather not burn hours just to get back to where I already was.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Funny enough, even ESLint’s latest releases remind me of this balance. They’ve added flat configs, better defaults, even multithreaded linting—but those changes showed me how much ecosystem readiness matters. A shiny new config system doesn’t help if plugins and tools aren’t ready to support it.&lt;/p&gt;

&lt;p&gt;So for now, I’m staying with the “boring but reliable” stack. BiomeJS looks promising, and I’ll keep an eye on it. But until it feels stable enough to trust in production, I’m not ready to make it my team’s daily driver.&lt;/p&gt;

&lt;p&gt;Sometimes boring tools are exactly what you need to keep shipping.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How JS Really Takes Photos!</title>
      <dc:creator>Mohammad</dc:creator>
      <pubDate>Sat, 02 Aug 2025 19:23:35 +0000</pubDate>
      <link>https://dev.to/mvahedii/how-js-really-takes-photos-498n</link>
      <guid>https://dev.to/mvahedii/how-js-really-takes-photos-498n</guid>
      <description>&lt;p&gt;There are two main ways to access the user's camera in JavaScript (and React). One of them became the go-to solution, and most libraries are just creative variations of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The video tag + Screenshot Trick
&lt;/h2&gt;

&lt;p&gt;You stream the camera into a video tag element, and when the user hits the shutter button, you capture a screenshot using a .&lt;br&gt;
📷 The image quality depends on the resolution you define — it’s usually lower quality, but fast and lightweight.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The ImageCapture API
&lt;/h2&gt;

&lt;p&gt;Instead of grabbing the whole screen, this API captures a single frame directly from the video stream.&lt;br&gt;
📸 The quality is much better, but unfortunately, it’s not supported in Safari or Firefox yet, and there’s no widely adopted library around it.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>api</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
