<?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: Ansh Saini</title>
    <description>The latest articles on DEV Community by Ansh Saini (@anshsaini).</description>
    <link>https://dev.to/anshsaini</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%2F230500%2F457006ba-6d72-441c-974b-cdc1374cc271.png</url>
      <title>DEV Community: Ansh Saini</title>
      <link>https://dev.to/anshsaini</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/anshsaini"/>
    <language>en</language>
    <item>
      <title>I built the running coach that actually listens to your body</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Sat, 17 Oct 2026 10:27:36 +0000</pubDate>
      <link>https://dev.to/anshsaini/i-built-the-running-coach-that-actually-listens-to-your-body-5bmd</link>
      <guid>https://dev.to/anshsaini/i-built-the-running-coach-that-actually-listens-to-your-body-5bmd</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I run with Nike Run Club. It tracks every run well, but its plans aren't personal. They don't know my heart rate zones, my actual paces, or what my last few weeks looked like. A tempo session says "comfortably hard" whether my resting heart rate is 50 or 70, and the plan doesn't notice when I miss a week or double my mileage. I wanted a coach that looks at my data and changes the plan because of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenStride&lt;/strong&gt; is an iOS running coach that reads your data from Apple Health and plans your week around it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Heart rate zones&lt;/strong&gt; from your resting HR and the highest HR seen in your own runs (Karvonen)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pace zones&lt;/strong&gt; from your best recent effort, or from pace plus heart rate when the effort wasn't all-out (Jack Daniels' VDOT)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Training load&lt;/strong&gt; per run and your 7-day vs 28-day load ratio&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HR drift&lt;/strong&gt; between the first and second half of each run&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How much of last week you actually did&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every week, Gemma (Google's open-weight model) reviews those signals, adjusts the plan, and tells you why in plain words. It runs on the iPhone through MLX, so the app needs no account or server. With the on-device model (the default), your health data stays on the phone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pricing
&lt;/h3&gt;

&lt;p&gt;One-time purchase on the App Store and Google Play: &lt;strong&gt;$10 (₹599 in India)&lt;/strong&gt;. No subscription or account needed.&lt;/p&gt;

&lt;p&gt;Adaptive training plans are already a paid category (Runna, TrainingPeaks), sold as monthly subscriptions. OpenStride can skip the subscription because the coach runs on the phone: after someone buys it, they cost nothing to serve. The app is iOS only today; the Play Store version comes with Android.&lt;/p&gt;

&lt;p&gt;Here's how it would look on the App Store (&lt;a href="https://ansh-saini.github.io/open-stride/appstore/" rel="noopener noreferrer"&gt;full sample listing&lt;/a&gt;):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5g4p4oajybgcxyqd9kkg.jpg" alt="A coach that reads your body" width="507" height="1100"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffwyqdybfuul097uwzrmc.jpg" alt="It notices when you overdo it" width="507" height="1100"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuw3k3tuc9in9jj64hcur.jpg" alt="Runs on your iPhone" width="507" height="1100"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Demo page with the full 60 s video: &lt;a href="https://ansh-saini.github.io/open-stride" rel="noopener noreferrer"&gt;ansh-saini.github.io/open-stride&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Recorded on my iPhone with a runner who doubled their mileage last week. Gemma 3 4B runs on the phone; the planning step is sped up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxsw28fx1cdg4m1o858sk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxsw28fx1cdg4m1o858sk.gif" alt="OpenStride demo" width="320" height="692"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The week&lt;/th&gt;
&lt;th&gt;A session&lt;/th&gt;
&lt;th&gt;Signals&lt;/th&gt;
&lt;th&gt;What the coach saw&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flvecnsc8ph41yvahmho3.png" alt="plan" width="800" height="1732"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flqdwi0wsll4a229tqo3u.png" alt="session" width="800" height="1732"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fabdg038jkeiuk80j63ym.png" alt="signals" width="800" height="1732"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo8k3hcfsenvfomsel3jd.png" alt="prompt" width="800" height="1732"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ansh-saini" rel="noopener noreferrer"&gt;
        ansh-saini
      &lt;/a&gt; / &lt;a href="https://github.com/ansh-saini/open-stride" rel="noopener noreferrer"&gt;
        open-stride
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/ansh-saini/open-stride/docs/media/icon-rounded.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fansh-saini%2Fopen-stride%2FHEAD%2Fdocs%2Fmedia%2Ficon-rounded.png" width="112" alt="OpenStride app icon"&gt;&lt;/a&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;OpenStride&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;An AI running coach that reads your body and runs on your iPhone.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;OpenStride reads your runs and heart rate from Apple Health, works out your zones and training load,&lt;br&gt;
and plans each week with Gemma 3, an open-weight model running on the phone.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/ansh-saini/open-stride#run-it" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f01932ee2a902540b8bf96fe6b27e3b5d0c4adb224cc9b5fc0d3f919e4bc31bc/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f694f532d31372532422d3131313131313f6c6f676f3d6170706c65266c6f676f436f6c6f723d7768697465" alt="iOS 17+"&gt;&lt;/a&gt;
&lt;a href="https://github.com/ansh-saini/open-stride#run-it" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/a3f17db6db6eac50ed70d7659405e193c69b1bb6968f6c72d04512876a9e8bfd/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f537769667455492d4865616c74684b69742d4630353133383f6c6f676f3d7377696674266c6f676f436f6c6f723d7768697465" alt="SwiftUI"&gt;&lt;/a&gt;
&lt;a href="https://github.com/ansh-saini/open-stride#how-it-works" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/6370ef1baae9e2aff8b6440487fce2c95ebc753e5fce3e0a9414a1a711bc13cd/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f47656d6d612532303325323034422d6f6e2d2d6465766963652d3432383546343f6c6f676f3d676f6f676c65266c6f676f436f6c6f723d7768697465" alt="Gemma 3 4B on-device"&gt;&lt;/a&gt;
&lt;a href="https://github.com/ml-explore/mlx-swift-lm" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/471e1072def41653ce33df732245fc6c630046749079c28dceee5046579e8a37/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d4c582d53776966742d464637413246" alt="MLX"&gt;&lt;/a&gt;
&lt;a href="https://github.com/ansh-saini/open-stride/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/592de2a92995a22de8864670ed8b2394217717ad764c008ee2a49f9791c780b5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d324541343446" alt="License: MIT"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://ansh-saini.github.io/open-stride" rel="nofollow noopener noreferrer"&gt;&lt;strong&gt;Demo video&lt;/strong&gt;&lt;/a&gt; &amp;nbsp;·&amp;nbsp; &lt;a href="https://ansh-saini.github.io/open-stride/appstore/" rel="nofollow noopener noreferrer"&gt;&lt;strong&gt;Store listing&lt;/strong&gt;&lt;/a&gt; &amp;nbsp;·&amp;nbsp; &lt;a href="https://github.com/ansh-saini/open-stride#how-it-works" rel="noopener noreferrer"&gt;&lt;strong&gt;How it works&lt;/strong&gt;&lt;/a&gt; &amp;nbsp;·&amp;nbsp; &lt;a href="https://github.com/ansh-saini/open-stride#results" rel="noopener noreferrer"&gt;&lt;strong&gt;Results&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;br&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/ansh-saini/open-stride/docs/appstore/web/appstore-1.jpg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fansh-saini%2Fopen-stride%2FHEAD%2Fdocs%2Fappstore%2Fweb%2Fappstore-1.jpg" width="15%" alt="A coach that reads your body"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/ansh-saini/open-stride/docs/appstore/web/appstore-2.jpg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fansh-saini%2Fopen-stride%2FHEAD%2Fdocs%2Fappstore%2Fweb%2Fappstore-2.jpg" width="15%" alt="It notices when you overdo it"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/ansh-saini/open-stride/docs/appstore/web/appstore-3.jpg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fansh-saini%2Fopen-stride%2FHEAD%2Fdocs%2Fappstore%2Fweb%2Fappstore-3.jpg" width="15%" alt="Your paces. Your zones."&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/ansh-saini/open-stride/docs/appstore/web/appstore-4.jpg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fansh-saini%2Fopen-stride%2FHEAD%2Fdocs%2Fappstore%2Fweb%2Fappstore-4.jpg" width="15%" alt="Zones built from your heart"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/ansh-saini/open-stride/docs/appstore/web/appstore-5.jpg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fansh-saini%2Fopen-stride%2FHEAD%2Fdocs%2Fappstore%2Fweb%2Fappstore-5.jpg" width="15%" alt="See exactly why it decided"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/ansh-saini/open-stride/docs/appstore/web/appstore-6.jpg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fansh-saini%2Fopen-stride%2FHEAD%2Fdocs%2Fappstore%2Fweb%2Fappstore-6.jpg" width="15%" alt="Runs on your iPhone"&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Nike Run Club plans are templates. They don't know your resting heart rate, your actual paces, or that you doubled your mileage last week. OpenStride builds the plan from your own numbers and changes it when they change.&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What it reads&lt;/th&gt;
&lt;th&gt;How&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Heart rate zones&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Resting HR and the highest HR seen in your runs&lt;/td&gt;
&lt;td&gt;Karvonen method&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pace zones&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Your best recent effort, or pace plus heart rate&lt;/td&gt;
&lt;td&gt;Jack Daniels' VDOT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Training load&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Every run's duration and heart rate&lt;/td&gt;
&lt;td&gt;Banister TRIMP, 7-day vs 28-day ratio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HR drift&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;First half vs second half of&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ansh-saini/open-stride" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;I created several modules which work together in steps to deliver an optimial running plan:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;CoachKit&lt;/strong&gt; (pure Swift, unit tested) computes zones, paces, load and drift, then builds a draft week: phase (base, build, peak, taper), weekly volume, a long run, quality sessions.&lt;/li&gt;
&lt;li&gt;Code turns the metrics into &lt;strong&gt;signals&lt;/strong&gt; with fixed thresholds, each one OK or FLAG. For example: &lt;code&gt;acute_chronic_load_ratio = 1.98 -&amp;gt; FLAG: above 1.5, injury risk&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma decides.&lt;/strong&gt; It sees the signals and the draft, and picks from a small action set: cut volume, slow easy pace, &lt;code&gt;make_easy&lt;/code&gt;, &lt;code&gt;shorten&lt;/code&gt; or &lt;code&gt;drop&lt;/code&gt; a day, each with a reason.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guardrails&lt;/strong&gt; apply the decision and override anything unsafe. No flag means the draft stands. A flag with too small a response gets a floor. The long run can't be dropped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma explains.&lt;/strong&gt; A second call writes the note from facts that code computed by diffing the draft against the final plan. Code writes the first sentence ("Your heart rate drifted 7.7% during recent runs."), and Gemma continues it. The note always matches what you see.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the model fails, a rule-based coach writes the week, so the week still gets planned.&lt;/p&gt;

&lt;h3&gt;
  
  
  An eval harness
&lt;/h3&gt;

&lt;p&gt;I built six synthetic runners with seeded histories and an expected coach behavior for each:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Runner&lt;/th&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;A good coach should&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;steady&lt;/td&gt;
&lt;td&gt;all in range&lt;/td&gt;
&lt;td&gt;keep progressing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;overreaching&lt;/td&gt;
&lt;td&gt;load ratio 1.98&lt;/td&gt;
&lt;td&gt;cut volume, max one hard day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;detrained&lt;/td&gt;
&lt;td&gt;21 days off&lt;/td&gt;
&lt;td&gt;no hard sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hr_drift&lt;/td&gt;
&lt;td&gt;7.7% drift&lt;/td&gt;
&lt;td&gt;slow easy pace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;missed_sessions&lt;/td&gt;
&lt;td&gt;did 20% of last week&lt;/td&gt;
&lt;td&gt;not add volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;beginner&lt;/td&gt;
&lt;td&gt;8 km a week&lt;/td&gt;
&lt;td&gt;progress gently&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;coach-eval&lt;/code&gt; runs them through Ollama, and &lt;code&gt;mlx-eval&lt;/code&gt; runs them through the exact MLX weights the phone uses. Each run is scored on whether the decision parsed, whether the final plan is right, whether the model got it right without a guardrail floor, and whether the note cites a real number.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Coach&lt;/th&gt;
&lt;th&gt;Runs&lt;/th&gt;
&lt;th&gt;Valid&lt;/th&gt;
&lt;th&gt;Final plan right&lt;/th&gt;
&lt;th&gt;Model right on its own&lt;/th&gt;
&lt;th&gt;Note matches plan&lt;/th&gt;
&lt;th&gt;Note cites the flagged number&lt;/th&gt;
&lt;th&gt;Avg time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;rules&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;0.0s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gemma3:1b (Ollama)&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;33%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.0s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gemma3:4b (Ollama)&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;4.8s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 3 4B (on-device)&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;8.0s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 3 1B (on-device)&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;33%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;4.3s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Real data is messier than test data
&lt;/h3&gt;

&lt;p&gt;My own runs exposed two things the personas didn't. None of my runs from the last four weeks have heart rate recorded, so there's no drift signal, and the coach has to work without it. And my "best effort" wasn't all-out, so the race formula underrated my fitness. Now, when heart rate exists, VDOT is also estimated from pace and heart rate reserve, and the higher estimate wins.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stack
&lt;/h3&gt;

&lt;p&gt;SwiftUI, HealthKit, Swift Charts, XCUITest on simulator and device, &lt;a href="https://github.com/ml-explore/mlx-swift-lm" rel="noopener noreferrer"&gt;MLX Swift LM&lt;/a&gt; with &lt;code&gt;mlx-community/gemma-3-text-4b-it-4bit&lt;/code&gt; (2.6 GB, downloaded once), Ollama for the simulator and evals, XcodeGen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Privacy that holds up.&lt;/strong&gt; Heart rate, resting heart rate and VO2 max are health data. With an open-weight model on the device, the coach has no server to send your data to. A closed API needs one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero marginal cost.&lt;/strong&gt; Every plan runs on the phone's GPU. No per-token bill means a solo developer can price this as a one-time purchase instead of a subscription that has to cover inference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Works offline.&lt;/strong&gt; After the one-time download it works on a trail with no signal, which is where runners are.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inspectable and swappable.&lt;/strong&gt; The Insights screen shows exactly what the coach saw and what it answered. Because the weights are open, I could test the same model on my Mac, in the simulator and on the phone, and I can swap in a newer Gemma or a fine-tune without changing the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Injury Prevention
&lt;/h2&gt;

&lt;p&gt;The most common mistake runners make is doing too much too fast. A good week feels great, so the next one gets longer and faster, and the body's tendons and bones can't keep up with the lungs. A static plan can't see that coming. OpenStride watches for it every week:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It measures the jump.&lt;/strong&gt; The load ratio compares your last 7 days of training to your 28-day average. Above 1.3, the coach cuts next week's volume by at least 10%. Above 1.5, it cuts at least 20% and allows only one hard session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It progresses slowly.&lt;/strong&gt; Weekly volume builds about 8% at a time from your 4-week average, so one big week doesn't become the new normal. A guardrail caps any week at 110% of that average plus 2 km, whatever the model asks for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It plans recovery weeks.&lt;/strong&gt; Every fourth week drops volume by 20%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It caps the long run.&lt;/strong&gt; The long run can't be more than 15% plus 1 km longer than your longest run in the last four weeks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It spaces hard days.&lt;/strong&gt; Hard sessions never fall on back-to-back days, there are at most two a week, and in the base phase, runners under 15 km a week get easy running only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It respects time off.&lt;/strong&gt; After 14 or more days without running, the plan starts from half your recent 4-week average with no hard sessions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It listens to your heart.&lt;/strong&gt; When your heart rate drifts more than 5% during easy runs, it slows your easy pace.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It reacts to missed weeks.&lt;/strong&gt; If you finished less than 60% of last week, the next week gets lighter instead of trying to catch up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the demo, the runner doubled their mileage in one week (load ratio 1.98). The draft plan had 31 km for the next week; the coach cut it to 25 km, kept one hard day, and explained why in the note.&lt;/p&gt;

&lt;h2&gt;
  
  
  My First User
&lt;/h2&gt;

&lt;p&gt;My first user is me. I ran the device test on my own iPhone with two months of my real runs: 22 runs, mostly 3 to 5 km, averaging about 14 km a week.&lt;/p&gt;

&lt;p&gt;What it got right: it planned four easy runs and a slightly longer Sunday run, about the same volume I've been doing, at an easy pace close to how I actually run. No signals were flagged, and the note said so instead of inventing a problem.&lt;/p&gt;

&lt;p&gt;What it taught me: the first estimate of my fitness was too low, because my "best" run wasn't an all-out effort. That's why the app now also estimates fitness from heart rate. And none of my runs since September 21 have heart rate recorded, so there's no drift to read and the coach is working with less than it could.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;I had a ton of fun building this project as it solves a real problem for me, and I know for a fact that many people face this problem. I'm super excited to integrate this with Apple Watch and seeing how that transforms my running experience. It will give me access to real-time heart rate tracking and session feedback, ensuring the coach receives complete data for more accurate planning. Maybe the coach can say "Hey you're going too fast, slow down" into my earphones during my run itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt;: Gemma 3 4B runs on the iPhone through MLX as the coach. Gemma 3 1B and Ollama-served Gemma are the fallbacks.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>ios</category>
    </item>
    <item>
      <title>Grow a Song: my run wrote its own soundtrack, with Gemma running on my phone</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Sat, 10 Oct 2026 08:38:35 +0000</pubDate>
      <link>https://dev.to/anshsaini/grow-a-song-my-run-wrote-its-own-soundtrack-with-gemma-running-on-my-phone-44ml</link>
      <guid>https://dev.to/anshsaini/grow-a-song-my-run-wrote-its-own-soundtrack-with-gemma-running-on-my-phone-44ml</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge, Week 1: Touch Grass&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;On Friday morning I went for a 35 minute run. When I started, my song was a single low drone. By the time I got back it had pads, a bass line, drums, a kalimba melody, and ten seconds of the actual street I ran down mixed in underneath.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grow a Song&lt;/strong&gt; turns time outside into music. Every minute you spend outdoors in daylight adds a layer to today's track:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Outside time (20 min target)&lt;/th&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0 min&lt;/td&gt;
&lt;td&gt;drone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 min&lt;/td&gt;
&lt;td&gt;pads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 min&lt;/td&gt;
&lt;td&gt;bass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8 min&lt;/td&gt;
&lt;td&gt;drums&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13 min&lt;/td&gt;
&lt;td&gt;melody&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20 min&lt;/td&gt;
&lt;td&gt;a field recording of where you are&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you stay inside all day, your song stays thin, and it only fills in once you get out. There's no way to skip ahead or buy layers, so the only way to hear the whole song is to go outside.&lt;/p&gt;

&lt;p&gt;The composer is &lt;strong&gt;Gemma 3 1B, running on my iPhone 13 Pro&lt;/strong&gt;. Before each walk or run it looks at the time of day, how high the sun is, what you're about to do, and how much you've been outside this week, then picks the key, scale, tempo, chord progression, instruments, a title and a one-line liner note. My run got this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Ephemeral Echoes&lt;/strong&gt;. D dorian, 160 bpm, chords 1 7 5 3, warm pads, pulse bass, driving drums, kalimba.&lt;br&gt;
&lt;em&gt;"The world stretches out beneath you, a slow, expectant invitation."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There's no account or server behind it, and after a one-time model download it works in airplane mode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/LCFFlzZQWFE" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Here is the song from my run, rendered by the same engine the app uses, plus a version that replays the run with the layers arriving one at a time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://res.cloudinary.com/dz05pa4ud/video/upload/v1791619582/2026-10-09_Ephemeral_Echoes_fvm7ke.m4a" rel="noopener noreferrer"&gt;Ephemeral Echoes (full song)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://res.cloudinary.com/dz05pa4ud/video/upload/v1791619583/2026-10-09_Ephemeral_Echoes_growth_ix2hzm.m4a" rel="noopener noreferrer"&gt;Ephemeral Echoes (how it grew, layer by layer)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The numbers from that morning, pulled off the phone:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;35.1 minutes counted as outside, 3.4 km&lt;/li&gt;
&lt;li&gt;all 6 layers playing by the end (my target was 20 minutes)&lt;/li&gt;
&lt;li&gt;a 10 second clip recorded at 7:40 AM, now the top layer of the song&lt;/li&gt;
&lt;li&gt;Gemma's field note afterwards: &lt;em&gt;"You ran 3.2 km. 'Ephemeral Echoes' is now a part of the recording."&lt;/em&gt; (Accurate in spirit. The distance is off by about 200 m, which is what you get from a 1B model writing journal entries.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ansh-saini" rel="noopener noreferrer"&gt;
        ansh-saini
      &lt;/a&gt; / &lt;a href="https://github.com/ansh-saini/grow-a-song" rel="noopener noreferrer"&gt;
        grow-a-song
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/ansh-saini/grow-a-song/App/Assets.xcassets/AppIcon.appiconset/icon.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fansh-saini%2Fgrow-a-song%2FHEAD%2FApp%2FAssets.xcassets%2FAppIcon.appiconset%2Ficon.png" width="140" alt="Grow a Song icon"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Grow a Song&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;
  Your time outside becomes music.&lt;br&gt;
  Every minute outdoors adds a layer to today's song, composed on your iPhone by an open-weight model
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://youtube.com/shorts/LCFFlzZQWFE" rel="nofollow noopener noreferrer"&gt;Demo video&lt;/a&gt; ·
  &lt;a href="https://res.cloudinary.com/dz05pa4ud/video/upload/v1791619582/2026-10-09_Ephemeral_Echoes_fvm7ke.m4a" rel="nofollow noopener noreferrer"&gt;Listen to a real run&lt;/a&gt; ·
  &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05" rel="nofollow"&gt;DEV challenge&lt;/a&gt;
&lt;/p&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Start a walk or a run. Your song begins as a single low drone. As you spend time outside,
new layers fade in:&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Share of your daily target&lt;/th&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0 %&lt;/td&gt;
&lt;td&gt;Drone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8 %&lt;/td&gt;
&lt;td&gt;Pads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25 %&lt;/td&gt;
&lt;td&gt;Bass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;42 %&lt;/td&gt;
&lt;td&gt;Drums&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;67 %&lt;/td&gt;
&lt;td&gt;Melody&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100 %&lt;/td&gt;
&lt;td&gt;A 10 second field recording of where you are&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Stay inside and your song stays thin. The only way to hear all of it is to go outside.&lt;/p&gt;
&lt;p&gt;Before each outing, &lt;strong&gt;Gemma 3 1B runs on the phone&lt;/strong&gt; and composes the song: key, scale
tempo, chord progression, instruments, a title and a one-line liner note, based on the
time of day, the sun's height, what…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ansh-saini/grow-a-song" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Swift, SwiftUI, AVAudioEngine, CoreLocation, CoreMotion, HealthKit and &lt;a href="https://github.com/ml-explore/mlx-swift-lm" rel="noopener noreferrer"&gt;MLX Swift LM&lt;/a&gt;. The music engine is a plain Swift package with 37 tests, so most of it runs and tests on a Mac without a phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Gemma composes, Swift plays
&lt;/h3&gt;

&lt;p&gt;I didn't want a 1B model generating audio or MIDI note by note. A model that small does fine at picking a mood and badly at keeping track of hundreds of notes, so Gemma makes about ten musical decisions and returns them as JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ephemeral Echoes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"D"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"scale"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dorian"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"bpm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"progression"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"pad"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"warm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"bass"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pulse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"drums"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"driving"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"melody"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"kalimba"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"liner_note"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The world stretches out beneath you, a slow, expectant invitation."&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A deterministic sequencer turns that into 16 bars of notes (melody in an A A' B A shape so it repeats enough to stick), and a small synth written from scratch plays it: detuned saw pads, an FM kalimba, a kick that's just a sine wave sweeping down from 155 Hz, and a reverb. The same spec always produces the same song, which turned out to matter later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting reliable JSON out of a 1B model
&lt;/h3&gt;

&lt;p&gt;My first eval on the Mac looked fine on the surface: 6 out of 6 songs were playable. Underneath, the schema-constrained decoding (MLX's &lt;code&gt;MLXGuidedGeneration&lt;/code&gt;, built on XGrammar) was failing every time, and a fallback parser was quietly rescuing it.&lt;/p&gt;

&lt;p&gt;Logging the partial output showed why. Gemma was emitting newline after newline. JSON allows unlimited whitespace, greedy decoding found that whitespace was always a legal next token, and it filled all 360 tokens with it. Adding the library's whitespace and closing-token biases fixed it:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Schema-guided success&lt;/td&gt;
&lt;td&gt;0 / 6&lt;/td&gt;
&lt;td&gt;6 / 6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average compose time (M2 Pro)&lt;/td&gt;
&lt;td&gt;9.5 s&lt;/td&gt;
&lt;td&gt;4.4 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The second problem was taste. Gemma picked D major pentatonic, chords 1 5 6 4, 60 bpm almost every time, including for a run. A 1B model leans hard on whichever option it reads first. So the prompt now shuffles the order of every option list, seeded by the current minute, and a small guard folds tempos into a range that fits the activity (a "75 bpm" run becomes 150). After that the same eval produced three keys, four scales and five different progressions in six songs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Knowing you're outside without a light sensor
&lt;/h3&gt;

&lt;p&gt;An Apple Watch measures time in daylight with its ambient light sensor, and the app reads that from HealthKit when it's there. But I wanted the app to work for anyone with just a phone, and iPhone apps can't read the phone's light sensor.&lt;/p&gt;

&lt;p&gt;So the phone infers it. A minute counts when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the sun is above civil twilight (-6 degrees), computed locally from time and location with the NOAA solar equations, and&lt;/li&gt;
&lt;li&gt;CoreMotion says you're walking, running or cycling, or the GPS fix is accurate to 20 m (usually means open sky) and you're away from home.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Driving never counts. On first launch, CoreMotion's 7-day activity history gives you a week of estimated songs straight away. With a watch, the measured number takes over.&lt;/p&gt;

&lt;h3&gt;
  
  
  The bug that would have ended my run
&lt;/h3&gt;

&lt;p&gt;I had a second AI agent review the code before the run. It found a real crash. iOS doesn't let background apps use the GPU. If I tapped Start and locked my phone while Gemma was still composing, MLX would hit that error and call &lt;code&gt;fatalError&lt;/code&gt;, killing the app, the music and the minute counter all at once, a few seconds into the run.&lt;/p&gt;

&lt;p&gt;The fix has three parts. Gemma only runs while the app is on screen, and a request made in the background waits until you open the app again. The screen stays awake for the few seconds composing takes. And every MLX call is wrapped in &lt;code&gt;withError&lt;/code&gt; so a GPU failure becomes a Swift error and falls back to a built-in song instead of a crash. The music starts immediately with the drone either way, and Gemma's song takes over at the next 16-bar phrase boundary so nothing cuts off mid-note.&lt;/p&gt;

&lt;p&gt;The same review caught that run progress was only saved when you tapped End, so it now saves every half minute.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting the song off the phone
&lt;/h3&gt;

&lt;p&gt;After the run I wanted the song on my laptop, but I'd never exported it. Because the renderer is deterministic, I copied the day's record and the field clip off the phone over a cable and rendered the identical song on the Mac with the same engine. That's the audio linked above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Built with an AI agent, tested by running
&lt;/h3&gt;

&lt;p&gt;I built this in one long session with Claude Code: plan, code, test, review, fix. It set up a small harness (&lt;code&gt;make test&lt;/code&gt;, &lt;code&gt;make render&lt;/code&gt;, &lt;code&gt;make eval&lt;/code&gt;, &lt;code&gt;make device&lt;/code&gt;) so every change got checked against unit tests, rendered audio levels, and real Gemma output before it reached my phone. Then the real test was mine: put my shoes on and press Start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;Grow a Song needs your location, your movement, sometimes your health data, and a microphone. That's a lot to hand to a server so it can pick a chord progression.&lt;/p&gt;

&lt;p&gt;With an open-weight model running locally, none of that leaves the phone. GPS, motion, daylight minutes and the street recordings stay on the device, and there's no backend that could leak them.&lt;/p&gt;

&lt;p&gt;It also works where runs actually happen. Trails and parks often have no signal, and after the 0.8 GB download the app doesn't need one.&lt;/p&gt;

&lt;p&gt;Running the model costs me nothing, so Gemma can compose before every walk instead of me rationing requests. And because the model is one line of config, I can move to a bigger Gemma once one fits comfortably on a phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt;: Gemma 3 1B (QAT, 4-bit) runs fully on device through MLX Swift LM and composes every song with schema-guided JSON output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of ElevenLabs&lt;/strong&gt;: narration for the demo video. The app itself makes no network calls.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Built with Claude Code. I directed the project, tested it on my own run, and reviewed and edited this post.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>hf26challenge</category>
      <category>devchallenge</category>
      <category>ai</category>
      <category>ios</category>
    </item>
    <item>
      <title>pfft...yet another finance tracker? think again :)</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Sun, 04 Oct 2026 18:05:20 +0000</pubDate>
      <link>https://dev.to/anshsaini/pfftyet-another-finance-tracker-think-again--5fni</link>
      <guid>https://dev.to/anshsaini/pfftyet-another-finance-tracker-think-again--5fni</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Built
&lt;/h2&gt;

&lt;p&gt;Living in India, we heavily utilise UPI for payments. The introduction of UPI, and 10-minute delivery apps have made spending money so easy and comfortable that it has made spendings get a little out of hand. We don't realise how much money we're spending on a day-to-day basis. I noticed my dear friend/colleague Anmol also struggling to keep track of where his money went.&lt;/p&gt;

&lt;p&gt;I personally have been using an Excel tracker for last few years. But I knew Anmol. He wouldn't find it appealing to manually categorize his expenses and create a budget. He's a guy who loves automations. He's the one who introduced me to the iOS Shortcuts app in the past. That got my gears turning. I knew I could use it to trigger an automation whenever a message arrives from a particular sender. I knew Anmol would want something that works seamlessly and requires minimal effort on his part.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I built a privacy focused finance tracker (pfft...). How it stands out from the rest?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No manual entries required. It automatically reads bank transactions through your text messages.&lt;/li&gt;
&lt;li&gt;Built with privacy in mind. The app never requests access to read all your messages. Just the ones you allow. Using "Automations" in the iOS Shortcuts app. The app only processes the messages you permit, ensuring your financial data remains private.&lt;/li&gt;
&lt;li&gt;Uses AI to auto categorise transactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/user-attachments/assets/09ba2353-03a1-4fe8-aa33-bf7e7eacd278" rel="noopener noreferrer"&gt;Click here to watch the video demo&lt;/a&gt;&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ansh-saini" rel="noopener noreferrer"&gt;
        ansh-saini
      &lt;/a&gt; / &lt;a href="https://github.com/ansh-saini/pfft" rel="noopener noreferrer"&gt;
        pfft
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;pfft&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;P&lt;/strong&gt;rivacy &lt;strong&gt;F&lt;/strong&gt;ocused &lt;strong&gt;F&lt;/strong&gt;inance &lt;strong&gt;T&lt;/strong&gt;racker. A personal finance tracker for India. An iPhone app reads bank SMS (ICICI, Axis), files each transaction into a category and asks you only when it isn't sure. The model that does the filing is open-weight and runs on a Mac at home, so no AI company sees your spending.&lt;/p&gt;

  
    
    &lt;span class="m-1"&gt;demo-compressed.mp4&lt;/span&gt;
  

  

  


&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;iPhone (Shortcuts automation on a bank SMS)
   -&amp;gt; iOS app "Log Transaction" intent
   -&amp;gt; POST /api/v1/ingest   (Next.js API on Vercel)
   -&amp;gt; parse the SMS, store it in Supabase
   -&amp;gt; rules and your own filing history first
   -&amp;gt; otherwise ask the model (Tailscale Funnel -&amp;gt; llama.cpp llama-server, Qwen3-4B-Instruct, on a Mac's GPU)
   -&amp;gt; notification: what it was filed as, or "what was this for?"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Most transactions never reach the model. A description you've used before, a merchant rule, a payment that recurs every month or a merchant you…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ansh-saini/pfft" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;Building this project required several moving parts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;parser: I created message parsers that extract key information such as transaction amount, merchant name, and date from incoming SMS messages.&lt;/li&gt;
&lt;li&gt;tagger: The categorisation is done by Qwen3-4B-Instruct, an open-weight model, served by llama.cpp's llama-server running on my own computer.&lt;/li&gt;
&lt;li&gt;A beautiful iOS App: Let's be honest, liquid glass feels absolutely amazing to use.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;Privacy. SMS Transactions are sensitive and valuable data. By keeping the processing local and using open-weight models, users retain control over their financial information without relying on third-party servers.&lt;/p&gt;

&lt;p&gt;My project idea is not unique. There are many products that do this. For example CRED Money. But they are highly invasive. They access your entire banking history, credit records, sometimes even full access to your email account to parse account statements.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New tech.&lt;/strong&gt; This is my first time using an open-weight model. I learned how to set up a local model on my machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud vs Local.&lt;/strong&gt; Running models on the cloud is kinda slow and made me appreciate the speed and efficiency of my local Apple M2 Chip.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tunneling.&lt;/strong&gt; First time tunneled my Mac to the cloud. Learned how to securely expose local services using Tailscale Funnel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Size matters.&lt;/strong&gt; I tried a 1.7B model to go faster. It read the prompt 2.5 times faster, then ignored the length limit and wrote a massive piece of junk; 75% of the answers were unusable. The 4B model was the smallest one that behaved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosting.&lt;/strong&gt; I tried hosting on Render, but the latency was too high for my use case, so I reverted to running the model locally.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Anmol's Concern
&lt;/h2&gt;

&lt;p&gt;Anmol, just like myself...is also a developer. The first question he asked me was, you're logging my transactions in your database? Privacy my foot!&lt;/p&gt;

&lt;p&gt;I assured him that on-device storage is on its way. I used a cloud database for the initial implementation but even my eventual goal is to keep all the data on-device to maximise privacy. At least for now I've swapped out my Supabase credentials for his. So he can sleep peacefully knowing I don't have eyes on his bank balance 👀&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks to Anmol
&lt;/h2&gt;

&lt;p&gt;Fun fact: Me being a web developer, this project was originally going to be a web app. But Anmol loves the Apple ecosystem. I knew he'd appreciate a clean liquid glass style apple design. He is always showing me examples of various apps in his phone "Look their UI is so clean, so beautiful", he says. So I knew this would be a big factor for him.&lt;br&gt;
I have never built a native iOS app in my lifetime. So thank you Anmol for pushing my boundaries as well with this project.&lt;/p&gt;

&lt;p&gt;Thank you for having the kind of taste that made me rebuild a webhook into a full-fledged iOS app.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>WorkFlow. Your AI Powered Secretary.</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Sun, 01 Sep 2024 17:01:00 +0000</pubDate>
      <link>https://dev.to/anshsaini/workflow-your-ai-powered-secretary-4np5</link>
      <guid>https://dev.to/anshsaini/workflow-your-ai-powered-secretary-4np5</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/nylas"&gt;Nylas Challenge&lt;/a&gt;: AI Expedition.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built and Why
&lt;/h2&gt;

&lt;p&gt;When you unlock the power of accessing people's email, the possibilities become endless. I've created an assistant which has the email context of not just yourself, but your entire company! So in essence, it can act as a knowledge powerhouse for your entire organization.&lt;/p&gt;

&lt;p&gt;WorkFlow is your ultimate personal secretary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Effortlessly find the best time to meet with anyone.&lt;/li&gt;
&lt;li&gt;Schedule meetings&lt;/li&gt;
&lt;li&gt;Keep track of your daily tasks.&lt;/li&gt;
&lt;li&gt;Instantly see who's on leave and even manage your own leaves.&lt;/li&gt;
&lt;li&gt;Get information about colleagues at your fingertips.&lt;/li&gt;
&lt;li&gt;Access critical company details like employee count, company vision, or who to reach out to for feedback.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the cherry on top? It’s Channel Agnostic ✨! Whether it’s WhatsApp, Slack, Discord, or even a website or mobile app, WorkFlow integrates seamlessly with whatever communication platform you’re already using. Just plug it in and let it do the work!&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;To bring this to life, I’ve set up a website where you can interact with the assistant. I’ve also integrated it with WhatsApp to demonstrate just how effortlessly it connects with popular communication platforms.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://workflow-ai-assistant.vercel.app/assistant" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fworkflow-ai-assistant.vercel.app%2Fimages%2Fog.jpg" height="auto" class="m-0"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://workflow-ai-assistant.vercel.app/assistant" rel="noopener noreferrer" class="c-link"&gt;
          WorkFlow
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Your AI Powered Secretary. Delegate your mundane tasks like scheduling meetings, getting task summary, etc to WorkFlow.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fworkflow-ai-assistant.vercel.app%2Ffavicon%2Ffavicon-16x16.png"&gt;
        workflow-ai-assistant.vercel.app
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Here's a video demo
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/aExiKc7WT7Q"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  WhatsApp Integration
&lt;/h4&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/3eYBB4hsvgE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ansh-saini" rel="noopener noreferrer"&gt;
        ansh-saini
      &lt;/a&gt; / &lt;a href="https://github.com/ansh-saini/workflow-assistant" rel="noopener noreferrer"&gt;
        workflow-assistant
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Your AI Powered Secretary.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;WorkFlow. Your AI Powered Secretary.&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/ansh-saini/workflow-assistant/main/public/images/og.jpg"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fansh-saini%2Fworkflow-assistant%2Fmain%2Fpublic%2Fimages%2Fog.jpg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I've created an assistant which has the email context of not just yourself, but your entire company! So in essence, it can act as a knowledge powerhouse for your entire organization.&lt;/p&gt;
&lt;p&gt;This assistant is your ultimate personal secretary:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Effortlessly find the best time to meet with anyone.&lt;/li&gt;
&lt;li&gt;Schedule meetings&lt;/li&gt;
&lt;li&gt;Keep track of your daily tasks.&lt;/li&gt;
&lt;li&gt;Instantly see who's on leave and even manage your own leaves.&lt;/li&gt;
&lt;li&gt;Get information about colleagues at your fingertips.&lt;/li&gt;
&lt;li&gt;Access critical company details like employee count, company vision, or who to reach out to for feedback.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And the cherry on top? It’s Channel Agnostic ✨! Whether it’s WhatsApp, Slack, Discord, or even a website or mobile app, this assistant integrates seamlessly with whatever communication platform you’re already using. Just plug it in and let it do the work!&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ansh-saini/workflow-assistant" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  My Journey
&lt;/h2&gt;

&lt;p&gt;The thing I loved about Nylas was the simplicity of APIs and SDKs. They were intuitive to use. I utilised various Nylas APIs to make the assistant capable of handling complex prompts.&lt;/p&gt;

&lt;p&gt;Development and testing with Nylas was also straightforward. I liked that I could jump straight in using Test Grants. There was no barrier to entry.&lt;/p&gt;

&lt;p&gt;It was a new experience for me to have control of users's email data. This project gave me a perspective on handling sensitive data and writing secure code with good practices.&lt;/p&gt;

&lt;p&gt;This was my first time building an AI Agent. I loved that, through the medium of this challenge, I was able to push the limits of my boundaries and dive into seemingly overwhelming topics like Embeddings and RAGs. I've personally learned A LOT throughout the process of building this project.&lt;/p&gt;

&lt;p&gt;Through this challenge I've come away with knowledge that I am surely going to implement in my personal as well work projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technology &amp;amp; Integrations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.nylas.com/docs/v3/calendar/" rel="noopener noreferrer"&gt;Nylas Calendar APIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.nylas.com/docs/v3/calendar/using-the-events-api/" rel="noopener noreferrer"&gt;Nylas Events APIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.nylas.com/docs/v3/email/" rel="noopener noreferrer"&gt;Nylas Messages APIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openai.com/" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/en-us" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Future Scope and Improvements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;In future, If Nylas rolls out support for Storage access like Google Drive, Microsoft OneDrive, It can easily be integrated into WorkFlow. This will allow users to share files using the assistant.&lt;/li&gt;
&lt;li&gt;Knowledge Store implementation using RAGs. Switching from a traditional database to a vector database will make the the assistant work much more efficiently and scale for large companies with hundreds of thousands of employees.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thank You
&lt;/h2&gt;

&lt;p&gt;A huge shoutout to Nylas and the DEV team for organizing such an incredible challenge! This experience has pushed me out of my comfort zone, allowing me to explore new technologies and grow as a developer.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>nylaschallenge</category>
      <category>api</category>
      <category>ai</category>
    </item>
    <item>
      <title>Free up some space on your VS Code</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Sun, 04 Aug 2024 08:35:20 +0000</pubDate>
      <link>https://dev.to/anshsaini/free-up-some-space-on-your-vs-code-2d0g</link>
      <guid>https://dev.to/anshsaini/free-up-some-space-on-your-vs-code-2d0g</guid>
      <description>&lt;p&gt;This is the first of a series of &lt;em&gt;opinionated&lt;/em&gt; posts that I'm going to make about becoming an absolute master of VSCode shortcuts. &lt;/p&gt;

&lt;p&gt;This Activity Bar is just something that takes up screen real estate and honestly its just useless.&lt;/p&gt;

&lt;p&gt;Just setup keyboard shortcuts for the tabs that you use. These are what I use.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Cmd + Shift + E&lt;/code&gt;: File Explorer&lt;br&gt;
&lt;code&gt;Cmd + Shift + F&lt;/code&gt;: Find in files (Already the default)&lt;br&gt;
&lt;code&gt;Cmd + Shift + G&lt;/code&gt;: Source Control&lt;br&gt;
&lt;code&gt;Cmd + Shift + A&lt;/code&gt;: Active pull request&lt;br&gt;
&lt;code&gt;Option + Shift + D&lt;/code&gt;: Debug Panel&lt;/p&gt;

&lt;p&gt;For panels which you use often, but not on the daily (for example: the extensions panel), you can just use the command palette. &lt;code&gt;Cmd + Shift + P&lt;/code&gt; and type 'focus on...'.&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%2F90b4vni9h6qcn13ng548.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%2F90b4vni9h6qcn13ng548.png" alt="command palette example" width="800" height="111"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can safely get rid of it and on the off chance you might need to access the Activity Bar, you can just use the command palette again to bring it back :)&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%2F8ck9yd2w24vihibt2421.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%2F8ck9yd2w24vihibt2421.png" alt="Hide activity bar" width="800" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>productivity</category>
      <category>programming</category>
      <category>developer</category>
    </item>
    <item>
      <title>Finally, a scalable folder structure for your frontend code</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Sun, 04 Aug 2024 06:32:41 +0000</pubDate>
      <link>https://dev.to/anshsaini/finally-a-scalable-folder-structure-for-your-frontend-code-43li</link>
      <guid>https://dev.to/anshsaini/finally-a-scalable-folder-structure-for-your-frontend-code-43li</guid>
      <description>&lt;h2&gt;
  
  
  Problem with "pages"
&lt;/h2&gt;

&lt;p&gt;Well, its simple. Not all features are pages. Then you end up developing features in your re-usable components folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;A folder-based structure which aims to keep a feature and its dependencies together. I'll be referring to this structure as &lt;strong&gt;The Base&lt;/strong&gt; in the rest of this post.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FolderName
├── index.tsx // Entry point
├── FolderName.module.css // Styles
├── components // Components that are specific to this feature
│   ├── ComponentName
│   │   ├── index.tsx
│   │   └── ComponentName.module.css
│   └── AnotherComponentName
│       ├── index.tsx
│       └── AnotherComponentName.module.css
├── hooks // Hooks that are specific to this feature
│   ├── useHookName.ts
│   └── useAnotherHookName.ts
├── utils // Utils that are specific to this feature
│   ├── utilName.ts
│   └── anotherUtilName.ts
└── assets // Assets that are specific to this feature
    └── someImage.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The beauty of this structure is that if you don't need something, just don't create the folder for it. &lt;/p&gt;

&lt;p&gt;Here's an example of a react app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src
├── components // Generic re-usable components
│   ├── ui
│   │   ├── Button
│   │   │   ├── index.tsx
│   │   │   └── Button.module.css
│   │   └── TextField
│   │       ├── index.tsx
│   │       └── TextField.module.css
│   └── Navigation
│       ├── Link
│       │   ├── index.tsx
│       │   └── Link.module.css
│       ├── BottomNavigation
│       │   ├── index.tsx
│       │   └── BottomNavigation.module.css
│       ├── TopNavigation
│       │   └── index.tsx
│       └── hooks
│           └── useExcludeShadow.ts
├── features
│   └── Notifications
│       ├── index.tsx
│       ├── Notifications.module.css
│       ├── components
│       │   ├── NotificationRowCard
│       │   │   └── index.tsx
│       │   ├── NotificationsList
│       │   │   ├── index.tsx
│       │   │   └── NotificationsList.module.css
│       │   └── NotificationsPopup
│       │       └── index.tsx
│       └── hooks
│           ├── useNotificationAction.ts
│           ├── useNotifications.ts
│           └── index.tsx
├── api
│   ├── axios.ts
│   └── resources
│       ├── auth
│       │   ├── index.ts
│       │   └── types.ts
│       ├── notifications
│       │   ├── index.ts
│       │   └── types.ts
│       ├── payment
│       │   ├── index.ts
│       │   └── types.ts
│       └── settings
│           ├── index.ts
│           └── types.ts
├── assets
│   ├── lotties
│   │   └── confetti.json
│   ├── icons
│   ├── placeholders
│   │   └── user-avatar-placeholder.svg
│   ├── fonts
│   │   ├── Inter.ttf
│   │   └── Switzer.ttf
│   ├── logos
│   │   ├── logo-primary.svg
│   │   ├── logo-stacked.svg
│   │   └── logo-alternate.svg
│   └── icons
│       ├── apple-icon.svg
│       ├── facebook-icon.svg
│       ├── google-icon.svg
│       ├── linkedin-icon.svg
│       ├── refresh-icon.svg
│       ├── telegram-icon.svg
│       └── whatsapp-icon.svg
├── global
│   ├── AppContext
│   │   ├── context.ts
│   │   └── index.tsx
│   ├── FeatureToggles
│   │   ├── useFeatureToggle.ts
│   │   └── index.tsx
│   └── UserPreferences
│       ├── useUserPreferences.ts
│       └── index.tsx
├── utils
│   ├── routes.ts
│   ├── formatDate.ts
│   └── hooks
│       ├── useArray.ts
│       └── useDeviceOrientation.ts
└── tracking
    ├── googleAnalytics.ts
    ├── firebaseTracking.ts
    └── useTracking.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure has been working and scaling well for us for over 3 years. &lt;br&gt;
Let me know if you guys want a detailed explanation about the api setup. Maybe I'll convert this post into a series of chapters.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>react</category>
    </item>
    <item>
      <title>Ready for the iPhone 16?</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Mon, 15 Jul 2024 00:15:33 +0000</pubDate>
      <link>https://dev.to/anshsaini/ready-for-the-iphone-16-4eah</link>
      <guid>https://dev.to/anshsaini/ready-for-the-iphone-16-4eah</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/wix"&gt;Wix Studio Challenge &lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Have you seen those long lines in front of stores before a sale is about to begin? That's exactly what I've built using Wix Studio.&lt;/p&gt;

&lt;p&gt;The website showcases a landing page where the upcoming product is revealed. In this case, The iPhone 16 Pro. You can join the queue, you'll be assigned a position in the queue. &lt;br&gt;
When the sale begins, you'll be emailed that its your turn to purchase. &lt;br&gt;
Then you can access the website and make the purchase.&lt;/p&gt;

&lt;p&gt;I've also added multiplayer cursors on the landing page for a sense of 'crowd'. I couldn't implement real-time cursors because I would need to use custom elements to make it work which was against the rules, so I had to compromise and just make some fake ones 😅.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://anshsaini625.wixstudio.io/online-storefront" rel="noopener noreferrer"&gt;Here's the link to the website&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Development Journey
&lt;/h2&gt;

&lt;p&gt;I was thinking of innovation on e-commerce websites, things that came to mind were 3D experiences within the website, or maybe Augmented Reality. I also thought of building a fancy landing page full of animations, but that wouldn't be something innovative in the shopping experience, that would just be a fancy landing page from which you click "Buy Now" and then its a standard checkout process.&lt;/p&gt;

&lt;p&gt;So I thought of making an online store-front for a product launch. People can see each other's cursors moving around and they can reserve their spot in the queue.&lt;/p&gt;

&lt;p&gt;This was my first time using Wix, or any low-code website builder.&lt;br&gt;
I spent a few days consuming wix tutorials and blogs. Exploring &lt;a href="https://dev.wix.com/docs/velo" rel="noopener noreferrer"&gt;Velo documentation&lt;/a&gt; to see what all possibilities are there. The tutorials were really helpful.&lt;/p&gt;

&lt;p&gt;Took me a few days to get the hang of it. It was a nice challenge to accomplish everything "The Wix Way". Using &lt;code&gt;$w&lt;/code&gt; instead of the JavaScript window.&lt;/p&gt;

&lt;p&gt;After the user is authenticated, I save their position in the queue in a Velo Database&lt;/p&gt;

&lt;h3&gt;
  
  
  APIs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Velo Members API&lt;/li&gt;
&lt;li&gt;Velo Databases and Permissions&lt;/li&gt;
&lt;li&gt;Velo Products&lt;/li&gt;
&lt;li&gt;Velo DOM apis to manipulate DOM elements&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Some feedback for Wix
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This was my first time using Wix Studio. The tutorial videos were really helpful, I didn't have to search for the tutorials for each thing I needed to do. They were right there in the platform itself. I loved that.&lt;/li&gt;
&lt;li&gt;The tutorials were well made and covered decent variety of use cases&lt;/li&gt;
&lt;li&gt;Managing store listings is really convenient. Adding products, managing inventory is really intuitive.&lt;/li&gt;
&lt;li&gt;Keyboard shortcuts for hiding/revealing the code editor. Much needed.&lt;/li&gt;
&lt;li&gt;There's no 'onMouseMove' event listener built-in to Velo. It prevented me from implementing real-time multiplayer cursors which I was really excited about. &lt;a href="https://forum.wixstudio.com/t/tracking-mouse-move-with-velo-code/35183" rel="noopener noreferrer"&gt;There is a workaround but it requires custom elements&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>wixstudiochallenge</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Recycling comes to WhatsApp ♻️</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Fri, 21 Jun 2024 03:44:11 +0000</pubDate>
      <link>https://dev.to/anshsaini/recycling-comes-to-whatsapp-63a</link>
      <guid>https://dev.to/anshsaini/recycling-comes-to-whatsapp-63a</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/twilio"&gt;Twilio Challenge v24.06.12&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built 🛠️
&lt;/h2&gt;

&lt;p&gt;A WhatsApp bot which gives you recycling ideas at your fingertips.&lt;br&gt;
WhatsApp is such a popular and convenient platform, that almost everyone has it. Just message the bot and tell it what you want to recycle. &lt;br&gt;
It works with text as well as images 🖼️✨&lt;/p&gt;
&lt;h3&gt;
  
  
  Motivation
&lt;/h3&gt;

&lt;p&gt;Often times while cleaning my house, I come across stuff I don't need anymore. Wouldn't it be nice to just snap a picture with the most used app on my phone and get instructions on how to recycle it? Well, think no more...&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo📱
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ansh-saini/recycle-whatsapp-bot?tab=readme-ov-file#just-tell-it-what-you-want-to-recycle" rel="noopener noreferrer"&gt;Watch the full working demo on GitHub&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiout7ht5vs0zbqqh4c5s.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiout7ht5vs0zbqqh4c5s.gif" alt="Working demo" width="222" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Currently the app is running in a sandbox environment. &lt;br&gt;
Text &lt;code&gt;join want-correctly&lt;/code&gt; to &lt;code&gt;+14155238886&lt;/code&gt; OR scan the QR code below.&lt;br&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%2Fh1tsf4zeqy9eqiiwuljc.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%2Fh1tsf4zeqy9eqiiwuljc.png" alt="QR code to try out the demo" width="452" height="442"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Link to code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ansh-saini" rel="noopener noreferrer"&gt;
        ansh-saini
      &lt;/a&gt; / &lt;a href="https://github.com/ansh-saini/recycle-whatsapp-bot" rel="noopener noreferrer"&gt;
        recycle-whatsapp-bot
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      This is my submission for the DEV Twilio Challenge
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;WhatsApp Recycle Bot ♻️&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;A WhatsApp bot which gives you recycling ideas at your fingertips.
WhatsApp is such a popular and convenient platform, that almost everyone has it. Just message the bot and tell it what you want to recycle.
It works with text as well as images 🖼️✨&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Just tell it what you want to recycle&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://private-user-images.githubusercontent.com/32511936/340074578-5eab6bc1-4f02-482a-b7ca-752ccc336e0d.gif?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3OTExOTE5MDgsIm5iZiI6MTc5MTE5MTYwOCwicGF0aCI6Ii8zMjUxMTkzNi8zNDAwNzQ1NzgtNWVhYjZiYzEtNGYwMi00ODJhLWI3Y2EtNzUyY2NjMzM2ZTBkLmdpZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjEwMDUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYxMDA1VDA5MTMyOFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWU4NmYwYWU5Mjg3N2FlMGIwYjQxNjNkZjU4ZmQ2MmFiODY4NDVhOWYzMDljMjEwMWIwZThmYzA1NTI1YWQyN2QmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRmdpZiJ9.Ujnxi1puf1cK51JBdkZN9ZdYwapUyVjdBo3X18WvydY"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fprivate-user-images.githubusercontent.com%2F32511936%2F340074578-5eab6bc1-4f02-482a-b7ca-752ccc336e0d.gif%3Fjwt%3DeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3OTExOTE5MDgsIm5iZiI6MTc5MTE5MTYwOCwicGF0aCI6Ii8zMjUxMTkzNi8zNDAwNzQ1NzgtNWVhYjZiYzEtNGYwMi00ODJhLWI3Y2EtNzUyY2NjMzM2ZTBkLmdpZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjEwMDUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYxMDA1VDA5MTMyOFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWU4NmYwYWU5Mjg3N2FlMGIwYjQxNjNkZjU4ZmQ2MmFiODY4NDVhOWYzMDljMjEwMWIwZThmYzA1NTI1YWQyN2QmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRmdpZiJ9.Ujnxi1puf1cK51JBdkZN9ZdYwapUyVjdBo3X18WvydY" alt="nailcutter-example"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Even works with images!&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://private-user-images.githubusercontent.com/32511936/340074824-11a034e5-acdc-48dc-bbff-39f03e7e95a5.gif?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3OTExOTE5MDgsIm5iZiI6MTc5MTE5MTYwOCwicGF0aCI6Ii8zMjUxMTkzNi8zNDAwNzQ4MjQtMTFhMDM0ZTUtYWNkYy00OGRjLWJiZmYtMzlmMDNlN2U5NWE1LmdpZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjEwMDUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYxMDA1VDA5MTMyOFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWNkMTIxYmM4Njc5MDE5YjJkOGRmODY5ZmUwYzZiODFlNjQ5YmJkNzRkYmJjNDYwZDljMGYxZDY4YzNhNzdkOWMmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRmdpZiJ9.2iNlQ6gJkksW2z18nhobTnOpVkZTWrRyG_21XIubBqg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fprivate-user-images.githubusercontent.com%2F32511936%2F340074824-11a034e5-acdc-48dc-bbff-39f03e7e95a5.gif%3Fjwt%3DeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3OTExOTE5MDgsIm5iZiI6MTc5MTE5MTYwOCwicGF0aCI6Ii8zMjUxMTkzNi8zNDAwNzQ4MjQtMTFhMDM0ZTUtYWNkYy00OGRjLWJiZmYtMzlmMDNlN2U5NWE1LmdpZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjEwMDUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYxMDA1VDA5MTMyOFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWNkMTIxYmM4Njc5MDE5YjJkOGRmODY5ZmUwYzZiODFlNjQ5YmJkNzRkYmJjNDYwZDljMGYxZDY4YzNhNzdkOWMmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRmdpZiJ9.2iNlQ6gJkksW2z18nhobTnOpVkZTWrRyG_21XIubBqg" alt="flask-example"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ansh-saini/recycle-whatsapp-bot" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  Twilio and AI ✨
&lt;/h2&gt;

&lt;p&gt;I used the &lt;a href="https://www.twilio.com/docs/messaging" rel="noopener noreferrer"&gt;Twilio Messaging API&lt;/a&gt; to setup communication through WhatsApp. I've used &lt;a href="https://cloud.google.com/vision/docs/drag-and-drop" rel="noopener noreferrer"&gt;Google Cloud Vision API&lt;/a&gt; to detect what object the sender has captured. Then I've engineered a prompt and fed that to &lt;a href="https://platform.openai.com/docs/models/gpt-3-5-turbo" rel="noopener noreferrer"&gt;Open AI's GPT 3.5 Turbo&lt;/a&gt; to get instructions on recycling the object.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Prize Categories 🏆
&lt;/h2&gt;

&lt;p&gt;Impactful Innovators &lt;/p&gt;

&lt;h2&gt;
  
  
  Some thoughts 🩵
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Working with Twilio has been quite easy. Things are pretty straight forward. I absolutely loved the fact that I didn't have to provide any billing information to get started. &lt;/li&gt;
&lt;li&gt;I was surprised by the simplicity of the steps needed to started with creating a WhatsApp Bot. Great job Twilio! Definitely going to continue using Twilio for building some cool stuff.&lt;/li&gt;
&lt;li&gt;Thank you DEV Team for organising awesome contests like these :)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>twiliochallenge</category>
      <category>ai</category>
      <category>twilio</category>
    </item>
    <item>
      <title>Deadlock</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Thu, 13 Jun 2024 02:57:29 +0000</pubDate>
      <link>https://dev.to/anshsaini/deadlocks-3h3n</link>
      <guid>https://dev.to/anshsaini/deadlocks-3h3n</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for DEV Computer Science Challenge v24.06.12: One Byte Explainer.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Explainer
&lt;/h2&gt;

&lt;p&gt;When two or more processes are unable to proceed because each is waiting for another to release a resource. It results in a stalemate where none can progress, requiring intervention like resource reallocation or termination to resolve.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>cschallenge</category>
      <category>computerscience</category>
      <category>beginners</category>
    </item>
    <item>
      <title>A headless hook-based exam proctoring system with React</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Thu, 16 Feb 2023 14:39:25 +0000</pubDate>
      <link>https://dev.to/anshsaini/a-headless-hook-based-exam-proctoring-system-with-react-20do</link>
      <guid>https://dev.to/anshsaini/a-headless-hook-based-exam-proctoring-system-with-react-20do</guid>
      <description>&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;I built a headless online exam proctoring system using React.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Force full-screen mode&lt;/li&gt;
&lt;li&gt;Warns when user switches tabs&lt;/li&gt;
&lt;li&gt;Prevents user from opening the context menu (menu opened by pressing right-click from the mouse)&lt;/li&gt;
&lt;li&gt;Prevents user from copying website content&lt;/li&gt;
&lt;li&gt;Works well with custom proctoring logic (which you might want to implement separately)&lt;/li&gt;
&lt;li&gt;Webcam detection (Coming Soon)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Category Submission
&lt;/h2&gt;

&lt;p&gt;SaaS Superstars✨&lt;/p&gt;

&lt;h2&gt;
  
  
  App Link
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://172.104.207.164/" rel="noopener noreferrer"&gt;Checkout the demo app here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Screenshots
&lt;/h2&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%2Fexxrz1wk0bh79n2m2zhc.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%2Fexxrz1wk0bh79n2m2zhc.png" alt="Begin Exam" width="800" height="328"&gt;&lt;/a&gt;&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%2Fkattztrgfzhdgi1ktcco.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%2Fkattztrgfzhdgi1ktcco.png" alt="Exam page" width="800" height="753"&gt;&lt;/a&gt;&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%2Fd4xqrld0jd0q9nw20dv7.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%2Fd4xqrld0jd0q9nw20dv7.png" alt="Full screen warning" width="800" height="584"&gt;&lt;/a&gt;&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%2Fjpfik0j7ldxsv15biqck.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%2Fjpfik0j7ldxsv15biqck.png" alt="Tab focus warning" width="800" height="757"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Description
&lt;/h2&gt;

&lt;p&gt;This is a headless proctoring library which only gives you some flags. What you do with that information is totally upto you. The UI for handling various use cases is completely in your hands. For more details, &lt;a href="https://github.com/ansh-saini/react-proctoring#overview" rel="noopener noreferrer"&gt;check out the project README&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Link to Source
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ansh-saini" rel="noopener noreferrer"&gt;
        ansh-saini
      &lt;/a&gt; / &lt;a href="https://github.com/ansh-saini/react-proctoring" rel="noopener noreferrer"&gt;
        react-proctoring
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Headless proctoring system for react
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Overview&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;This is a headless library which only gives you some flags. What you do with that information is totally upto you. The UI for handling various use cases is completely in your hands.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Features&lt;/h1&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Force full-screen mode&lt;/li&gt;
&lt;li&gt;Warns when user switches tabs&lt;/li&gt;
&lt;li&gt;Prevents user from opening the context menu (menu opened by pressing right-click from the mouse)&lt;/li&gt;
&lt;li&gt;Prevents user from copying website content&lt;/li&gt;
&lt;li&gt;Works well with custom proctoring logic (which you might want to implement separately)&lt;/li&gt;
&lt;li&gt;Webcam detection (Coming Soon)&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why should you use this?&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;There are lots of browser specific issues that show up when you are working with browser APIs. This package tries to cover most if not all of them. So you can focus on whats actually important for your business. All features are set up in a way that you can opt out of any feature that you do not wish to use.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Why do&lt;/h3&gt;…&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ansh-saini/react-proctoring" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I was looking for a ready-to-use proctoring system for an online exam. I found a few solutions but all of them were coupled very closely with the UI. I could not find a headless proctoring system. So, I decided to build my own. It is perfect for having full control over the UI while reaping all the benefits of a proctoring system.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I built it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I utilized Linode's web server to deploy the demo application to the web. I had never configured a web server before. I learnt how to do that from Linode's awesome documention.&lt;/li&gt;
&lt;li&gt;I'm thinking of adding a face detection feature to this library. I'm planning to host machine learning models on Linode then use it as an API in this library.&lt;/li&gt;
&lt;li&gt;I learned about various browser APIs. Like page visibility detection, requesting camera permissions, requesting full screen permission from the browser etc.&lt;/li&gt;
&lt;li&gt;I learned about ways of getting around cross-browser issues when it comes to native APIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Final thoughts
&lt;/h3&gt;

&lt;p&gt;This was my first time using Linode. Things were very easy to setup. I followed &lt;a href="https://www.linode.com/docs/guides/how-to-deploy-a-react-app-on-debian-10/" rel="noopener noreferrer"&gt;this guide&lt;/a&gt; to understand how to deploy my application. I think Linode is a great platform and I'm definitely going to figure out how to utilize my free $100 credit in a better way 😁&lt;/p&gt;

</description>
      <category>mentorship</category>
      <category>career</category>
      <category>discuss</category>
    </item>
    <item>
      <title>How to download fancy QR Codes with React</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Sun, 08 Jan 2023 10:48:45 +0000</pubDate>
      <link>https://dev.to/anshsaini/how-to-download-fancy-qr-codes-with-react-b25</link>
      <guid>https://dev.to/anshsaini/how-to-download-fancy-qr-codes-with-react-b25</guid>
      <description>&lt;p&gt;Generating QR codes on the web is easy. There are lots of libraries out there who help us do it. One of them is &lt;a href="https://www.npmjs.com/package/qrcode.react" rel="noopener noreferrer"&gt;qrcode.react&lt;/a&gt; which we will be using for the purpose of this tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code to download a plain QR Code
&lt;/h2&gt;

&lt;p&gt;We're rendering a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element in the DOM using &lt;code&gt;QRCodeCanvas&lt;/code&gt; supplied by &lt;code&gt;qrcode.react&lt;/code&gt;. We can convert it to a &lt;code&gt;png&lt;/code&gt; using JavaScript and then download it as a file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// QRCodeDownload.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&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;QRCodeCanvas&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="s2"&gt;qrcode.react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;components/Button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;QRCodeDownload&lt;/span&gt; &lt;span class="o"&gt;=&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://anshsaini.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;downloadQRCode&lt;/span&gt; &lt;span class="o"&gt;=&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;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;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#qrcode-canvas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLCanvasElement&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;canvas&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="s2"&gt;&amp;lt;canvas&amp;gt; not found in the DOM&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;pngUrl&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;toDataURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/png&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="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/octet-stream&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;downloadLink&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;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;downloadLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pngUrl&lt;/span&gt;
    &lt;span class="nx"&gt;downloadLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;download&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;QR code.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;downloadLink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;downloadLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;downloadLink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"p-3"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;QRCodeCanvas&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"qrcode-canvas"&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"H"&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"my-5"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;downloadQRCode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Download QR Code&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;QRCodeDownload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fkuu7f96ktpksg5a4sdv6.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%2Fkuu7f96ktpksg5a4sdv6.png" alt="QR Code downloaded" width="734" height="962"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voila✨ The QR Code is now saved in the file system as an image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking it a step further
&lt;/h2&gt;

&lt;p&gt;No one wants to share a QR code on its own. Its ugly. We want a nice UI surrounding the QR code, giving it some context. Maybe something like this:&lt;br&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%2Ffb4o9tc6jad4vfuctig0.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%2Ffb4o9tc6jad4vfuctig0.png" alt="Fancy QR Code" width="556" height="810"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's a create a wrapper component which will handle the styling. I'm using &lt;code&gt;react-jss&lt;/code&gt; for styling but of course you can use whatever you want and style your QR Code however you like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// QRCodeTemplate.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PropsWithChildren&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="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;DEV_RAINBOW_LOGO&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assets/qr_code/dev-rainbow.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;DEV_RAINBOW_BG&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assets/qr_code/dev-rainbow-bg.png&lt;/span&gt;&lt;span class="dl"&gt;"&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;createUseStyles&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="s2"&gt;react-jss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Typography&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;components/Typography&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ICON_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createUseStyles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;backgroundImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`url('&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;DEV_RAINBOW_BG&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="na"&gt;maxWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1060&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;maxHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1521&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;32px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5px solid #EFEFF7&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;logo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;boxShadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;9px 3px 20px 1px rgb(0 0 0 / 10%)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICON_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICON_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;absolute&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// Need to offset the values due to `excavate: true` in qrcode.react&lt;/span&gt;
    &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`calc(50% - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ICON_SIZE&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&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="s2"&gt;px)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// Need to offset the values due to `excavate: true` in qrcode.react&lt;/span&gt;
    &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`calc(50% - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ICON_SIZE&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;qrContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;relative&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#EFEFF7&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;56px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&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="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spacing&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="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;qrInner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;32px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;referredBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fontStyle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;normal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fontWeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;600&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;24px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lineHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;29px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;letterSpacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.1em&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;QRCodeTemplate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PropsWithChildren&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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;classes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useStyles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"fancy-qr-code"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"logo"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;DEV_RAINBOW_LOGO&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"mt-7"&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"title1"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        To register, scan the QR Code.
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;qrContainer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt;
          &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;
          &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ICON_SIZE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;DEV_RAINBOW_LOGO&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ICON_SIZE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;qrInner&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;referredBy&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"emphasised"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        REFERRED BY
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"largeTitle"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Ansh Saini&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;QRCodeTemplate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now pass our QR Code as children to the &lt;code&gt;QRCodeTemplate&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;QRCodeTemplate&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;QRCodeCanvas&lt;/span&gt;
    &lt;span class="c1"&gt;// Passing image here doesn't render when we convert html to canvas. So, we handle it manually.&lt;/span&gt;
    &lt;span class="c1"&gt;// We are only using this prop for `excavate: true` use case.&lt;/span&gt;
    &lt;span class="na"&gt;imageSettings&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&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="kc"&gt;undefined&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="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;excavate&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="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"H"&lt;/span&gt;
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;QRCodeTemplate&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Earlier we only had a canvas element in the DOM so we could directly convert it to a &lt;code&gt;png&lt;/code&gt;. But now, our DOM includes HTML elements like &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; etc. for styling. So we need to parse our DOM as &lt;code&gt;canvas&lt;/code&gt; and then convert it into a &lt;code&gt;png&lt;/code&gt;. There's a libary which helps us do just that. &lt;a href="https://html2canvas.hertzen.com/" rel="noopener noreferrer"&gt;html2canvas&lt;/a&gt;. We modify our &lt;code&gt;downloadQRCode&lt;/code&gt; function such that first we get snapshot of our DOM as a &lt;code&gt;canvas&lt;/code&gt; then we convert that into a &lt;code&gt;png&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// QRCodeTemplate.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;html2canvas&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;html2canvas&lt;/span&gt;&lt;span class="dl"&gt;"&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;QRCodeCanvas&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="s2"&gt;qrcode.react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;components/Button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;QRCodeTemplate&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;components/dashboard/QRCodeTemplate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;QRCodeDownload&lt;/span&gt; &lt;span class="o"&gt;=&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://anshsaini.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getCanvas&lt;/span&gt; &lt;span class="o"&gt;=&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;qr&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="s2"&gt;fancy-qr-code&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;qr&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;return&lt;/span&gt; &lt;span class="nf"&gt;html2canvas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;qr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;onclone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;snapshot&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;qrElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;snapshot&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="s2"&gt;fancy-qr-code&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;qrElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="c1"&gt;// Make element visible for cloning&lt;/span&gt;
        &lt;span class="nx"&gt;qrElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;downloadQRCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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;canvas&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;getCanvas&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;canvas&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="s2"&gt;&amp;lt;canvas&amp;gt; not found in DOM&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;pngUrl&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;toDataURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/png&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="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/octet-stream&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;downloadLink&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;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;downloadLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pngUrl&lt;/span&gt;
    &lt;span class="nx"&gt;downloadLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;download&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;QR code.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;downloadLink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;downloadLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;downloadLink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"p-3"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;QRCodeTemplate&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;QRCodeCanvas&lt;/span&gt;
          &lt;span class="c1"&gt;// Passing image here doesn't render when we convert html to canvas. So, we handle it manually.&lt;/span&gt;
          &lt;span class="c1"&gt;// We are only using this prop for `excavate: true` use case.&lt;/span&gt;
          &lt;span class="na"&gt;imageSettings&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&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="kc"&gt;undefined&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="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;excavate&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="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"H"&lt;/span&gt;
          &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;QRCodeTemplate&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"my-5"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;downloadQRCode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Download QR Code&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;QRCodeDownload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;We have a beautiful QR code saved as a &lt;code&gt;png&lt;/code&gt;. And with the power of React, we can create as many styles as we want.&lt;br&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%2F7qy22fyjzjhuu85lkecg.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%2F7qy22fyjzjhuu85lkecg.png" alt="Fancy QR Code downloaded" width="573" height="967"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gratitude</category>
    </item>
    <item>
      <title>Understanding stops property on LinearGradient in Flutter</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Thu, 05 Jan 2023 05:53:31 +0000</pubDate>
      <link>https://dev.to/anshsaini/understanding-stops-property-on-lineargradient-in-flutter-1fnk</link>
      <guid>https://dev.to/anshsaini/understanding-stops-property-on-lineargradient-in-flutter-1fnk</guid>
      <description>&lt;p&gt;This is what the &lt;a href="https://api.flutter.dev/flutter/painting/Gradient/stops.html" rel="noopener noreferrer"&gt;official docs&lt;/a&gt; say&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A list of values from 0.0 to 1.0 that denote fractions along the gradient.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Lets say we have to create a bar which has a gradient going from red -&amp;gt; green -&amp;gt; blue -&amp;gt; black.&lt;br&gt;
The percentages are as follows:&lt;br&gt;
10% red&lt;br&gt;
30% green&lt;br&gt;
40% blue&lt;br&gt;
Rest of it black (20%)&lt;/p&gt;

&lt;p&gt;We might write something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;LinearGradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;colors:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nl"&gt;stops:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.4&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="mf"&gt;0.2&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;Here's what we get:&lt;br&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%2Fbol9p0kqqywcxsyyz3a5.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%2Fbol9p0kqqywcxsyyz3a5.png" alt="Incorrect gradient" width="800" height="112"&gt;&lt;/a&gt;Of course this is not what we want.&lt;/p&gt;
&lt;h3&gt;
  
  
  How to fix it?
&lt;/h3&gt;

&lt;p&gt;Before we jump into the solution let's look at the problematic code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;LinearGradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;colors:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nl"&gt;stops:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.4&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="mf"&gt;0.2&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;We have supplied &lt;code&gt;stops=[0.1, 0.4, 0.5, 0.2]&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  How the &lt;code&gt;stops&lt;/code&gt; property works
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;stops&lt;/code&gt; property requires us to specify where the color should start from. So, if we have two colors, red and black and we want both of them to take 50% of the container, we can write&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;LinearGradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;colors:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nl"&gt;stops:&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="mf"&gt;0.6&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;Notice that I wrote &lt;code&gt;0.6&lt;/code&gt; instead of &lt;code&gt;0.5&lt;/code&gt;. If we write &lt;code&gt;0.5&lt;/code&gt;, then it would look like a 40-60 split instead of 50-50. &lt;/p&gt;

&lt;p&gt;0 to 0.5 would be red, then 0.6 to 1 would be black.&lt;br&gt;
The following code would also give us the same result&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;LinearGradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;colors:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nl"&gt;stops:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.6&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 is because &lt;a href="https://api.flutter.dev/flutter/painting/Gradient/stops.html#:~:text=If%20the%20first%20value%20is%20not%200.0%2C%20then%20a%20stop%20with%20position%200.0%20and%20a%20color%20equal%20to%20the%20first%20color%20in%20colors%20is%20implied" rel="noopener noreferrer"&gt;as the docs mention&lt;/a&gt;, if the &lt;code&gt;stops&lt;/code&gt; array doesn’t start with a &lt;code&gt;0&lt;/code&gt;, it implicitly means that it would start with &lt;code&gt;0&lt;/code&gt; and the first color in the &lt;code&gt;colors&lt;/code&gt; array. Same thing happens with last value of &lt;code&gt;stops&lt;/code&gt; array. It should either explicitly end with a &lt;code&gt;1&lt;/code&gt;, or flutter will implicitly consider it as &lt;code&gt;1&lt;/code&gt; with last color supplied in the &lt;code&gt;colors&lt;/code&gt; array. So, actually flutter will interpret it as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;LinearGradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;colors:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nl"&gt;stops:&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="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.6&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="p"&gt;),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fyt49oxvheude5iaub7js.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%2Fyt49oxvheude5iaub7js.png" alt="Gradient Examples" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing the issue
&lt;/h2&gt;

&lt;p&gt;Now that we know how the &lt;code&gt;stops&lt;/code&gt; property works, we can get our desired gradient.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;LinearGradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;colors:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nl"&gt;stops:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.8&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="p"&gt;),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fgbdllup5oq422euoi0z5.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%2Fgbdllup5oq422euoi0z5.png" alt="Gradient fixed" width="800" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Playground
&lt;/h2&gt;

&lt;p&gt;I made a &lt;a href="https://dartpad.dev/?id=9c577bf3dcebf1a5aae37a5cac281761" rel="noopener noreferrer"&gt;dartpad link&lt;/a&gt; if you want to play around with the examples above.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>beginners</category>
      <category>dart</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
