<?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: khuram shahzad</title>
    <description>The latest articles on DEV Community by khuram shahzad (@khuram_shahzad_93fd7f6746).</description>
    <link>https://dev.to/khuram_shahzad_93fd7f6746</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%2F4214020%2F7c35f880-2422-4d48-8990-35628e5686c3.jpg</url>
      <title>DEV Community: khuram shahzad</title>
      <link>https://dev.to/khuram_shahzad_93fd7f6746</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/khuram_shahzad_93fd7f6746"/>
    <language>en</language>
    <item>
      <title>How I Fixed the EXIF Orientation Bug in My Browser-Based Image Resizer</title>
      <dc:creator>khuram shahzad</dc:creator>
      <pubDate>Thu, 29 Oct 2026 17:34:38 +0000</pubDate>
      <link>https://dev.to/khuram_shahzad_93fd7f6746/how-i-fixed-the-exif-orientation-bug-in-my-browser-based-image-resizer-52ni</link>
      <guid>https://dev.to/khuram_shahzad_93fd7f6746/how-i-fixed-the-exif-orientation-bug-in-my-browser-based-image-resizer-52ni</guid>
      <description>&lt;p&gt;A few days ago, a user reached out with a frustrating bug report for my free browser-based Image Resizer.&lt;/p&gt;

&lt;p&gt;"I uploaded a photo from my iPhone, resized it, but now my face is sideways."&lt;/p&gt;

&lt;p&gt;If you've ever built an image processing tool, you know exactly what this is: the EXIF orientation bug.&lt;/p&gt;

&lt;p&gt;Here is why it happens, and how I fixed it.&lt;/p&gt;

&lt;p&gt;The "It Works on My Machine" Problem&lt;br&gt;
When you upload a photo from a modern smartphone, the image file is often saved in landscape (e.g., 4032×3024). But if you held the phone vertically, the camera doesn't actually rotate the pixels. Instead, it writes a tiny piece of metadata called an &lt;strong&gt;EXIF Orientation tag&lt;/strong&gt; (usually &lt;code&gt;6&lt;/code&gt; or &lt;code&gt;8&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The phone's gallery app reads this tag and says, "Ah, I need to rotate this 90 degrees before showing it to the user."&lt;/p&gt;

&lt;p&gt;But when you load that image into an HTML &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; using the classic method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ... later ...&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newHeight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The canvas &lt;strong&gt;ignores the EXIF data&lt;/strong&gt;. It just reads the raw pixels (landscape) and draws them. That's why the resized image looks sideways.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Old Way (and why it's a headache)
&lt;/h4&gt;

&lt;p&gt;For years, developers had to parse the image file's binary data manually using libraries like &lt;code&gt;exif-js&lt;/code&gt;, read the orientation tag, and then write complex math inside the canvas context to manually rotate, flip, or mirror the image.&lt;/p&gt;

&lt;p&gt;It worked, but it was a lot of code and prone to breaking.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Modern Fix: createImageBitmap()
&lt;/h4&gt;

&lt;p&gt;Modern browsers have a much better API for this: &lt;code&gt;createImageBitmap()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Instead of just loading an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag, you can pass the raw &lt;code&gt;File&lt;/code&gt; object directly to this function and pass a configuration object with it. The magic property is &lt;code&gt;imageOrientation: "from-image"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is how the logic looks in my tool now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// The browser reads the file AND respects the EXIF data&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bitmap&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;createImageBitmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;imageOrientation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;from-image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="c1"&gt;// Now the width and height are correct, and the pixels are rotated&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Pass the bitmap to the canvas...&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;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;canvas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;originalWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;originalHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// ... rest of the resizing logic&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Could not read image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By adding &lt;code&gt;imageOrientation: "from-image"&lt;/code&gt;, the browser handles the EXIF rotation for you. It literally takes an iPhone photo taken in portrait mode and hands you a perfectly upright &lt;code&gt;ImageBitmap&lt;/code&gt; ready for the canvas.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Catch: Fallback for Older Browsers
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;createImageBitmap&lt;/code&gt; with the &lt;code&gt;imageOrientation&lt;/code&gt; option is well-supported in modern Chrome, Edge, Firefox, and Safari. However, if you need to support very old browsers, you still have to fall back to the older new &lt;code&gt;Image()&lt;/code&gt; method (which means the image might look sideways on ancient devices).&lt;/p&gt;

&lt;p&gt;For my tool, I decided the trade-off was worth it. The vast majority of users are on modern browsers, and fixing the bug for 99% of users was better than keeping complex legacy code.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Conclusion
&lt;/h4&gt;

&lt;p&gt;If you are building an image resizer, cropper, or compressor in the browser, don't rely on the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag if you are dealing with mobile uploads. Use &lt;code&gt;createImageBitmap&lt;/code&gt; and let the browser do the heavy lifting.&lt;/p&gt;

&lt;p&gt;It is one line of code that saved me hours of debugging and a lot of math.&lt;/p&gt;

&lt;p&gt;If you want to see the tool in action, you can try it here: &lt;a href="https://toolsvibe.online/tools/image-resizer/" rel="noopener noreferrer"&gt;https://toolsvibe.online/tools/image-resizer/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have you dealt with the EXIF orientation bug before? How did you solve it?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>showdev</category>
      <category>imageprocessing</category>
    </item>
    <item>
      <title>Built Free, Privacy-First Tools for Pakistan Tax &amp; Zakat (They Never Touch a Server)</title>
      <dc:creator>khuram shahzad</dc:creator>
      <pubDate>Thu, 29 Oct 2026 00:25:42 +0000</pubDate>
      <link>https://dev.to/khuram_shahzad_93fd7f6746/built-free-privacy-first-tools-for-pakistan-tax-zakat-they-never-touch-a-server-2nin</link>
      <guid>https://dev.to/khuram_shahzad_93fd7f6746/built-free-privacy-first-tools-for-pakistan-tax-zakat-they-never-touch-a-server-2nin</guid>
      <description>&lt;p&gt;I have a small ritual every tax season in Pakistan.&lt;/p&gt;

&lt;p&gt;I open a calculator. I enter my salary. I wait. Then the result is locked behind an email signup, or the page is covered in ads that make my phone lag, or the calculator itself is using tax slabs from three years ago.&lt;/p&gt;

&lt;p&gt;I got tired of it. So I built the version I wanted to exist: &lt;strong&gt;ToolsVibe&lt;/strong&gt; — a collection of free tools that run entirely in your browser. No uploads, no signups, no "create an account to see your result."&lt;/p&gt;

&lt;p&gt;This post is about why I built it this way, what is inside, and one technical decision that made it possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  The privacy thing is not a promise. It is architecture.
&lt;/h3&gt;

&lt;p&gt;Every tool site asks you to trust their privacy policy. Policies are words. Architecture is physics.&lt;/p&gt;

&lt;p&gt;When a tool runs client-side, the browser reads your input, processes it in JavaScript, and hands you the result. There is no upload endpoint. There is nothing to trust because your data never leaves your device. You can open the network tab and watch: the only thing that loads is the page itself.&lt;/p&gt;

&lt;p&gt;That constraint shaped everything. For an image resizer, the browser reads the file, draws it onto a canvas, resizes it, and gives you a download. For a tax calculator, the numbers are processed in a JavaScript function and rendered back to you. No server round trip. No file sitting in someone else's storage.&lt;/p&gt;

&lt;p&gt;I used &lt;strong&gt;Astro&lt;/strong&gt; for the site. It ships zero JavaScript by default and only loads the interactive scripts when you actually use a tool. For a tools site, that means the landing pages load fast and the heavy lifting only happens on the pages that need it.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is inside (the short list)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pakistan Tax &amp;amp; Zakat:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Salary Tax Calculator&lt;/strong&gt; — take-home salary, FBR income tax, EOBI deductions, and slab breakdown for Tax Year 2027.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Freelancer Tax Calculator&lt;/strong&gt; — IT export income and local freelance income.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zakat Calculator&lt;/strong&gt; — cash, gold, silver, and investments, with the Nisab calculation shown.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EMI Calculator&lt;/strong&gt; — loan monthly payment, total interest, and yearly breakdown.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Image Tools:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Image Compressor&lt;/strong&gt;, &lt;strong&gt;Image Resizer&lt;/strong&gt;, &lt;strong&gt;Image Cropper&lt;/strong&gt;, &lt;strong&gt;Image Converter&lt;/strong&gt; — all the "this file is too big" and "this image is the wrong size" problems, without uploading your photos to a stranger's server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Writing &amp;amp; Dev:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Word Counter&lt;/strong&gt;, &lt;strong&gt;JSON Formatter&lt;/strong&gt;, &lt;strong&gt;QR Code Generator&lt;/strong&gt;, &lt;strong&gt;Password Generator&lt;/strong&gt;, &lt;strong&gt;Unit Converter&lt;/strong&gt;, &lt;strong&gt;Age Calculator&lt;/strong&gt;, &lt;strong&gt;Percentage Calculator&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see the full list here: &lt;a href="https://dev.tourl"&gt;https://toolsvibe.online/tools/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  One technical thing I learned
&lt;/h3&gt;

&lt;p&gt;The hardest part of client-side tools is not the logic. It is &lt;strong&gt;memory&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A browser tab has limits. If you try to resize an 8000×6000 photo in one jump, the phone will struggle. The fix I used in the image resizer is &lt;strong&gt;progressive downscaling&lt;/strong&gt;: if the target is less than half the original, halve it once, then again, until the image is close to the target size, then draw it once at the exact size. The edges stay sharper and the browser does not crash.&lt;/p&gt;

&lt;p&gt;It is a small thing. But it is the difference between a tool that works on a phone and a tool that only works on a laptop.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I am not doing (yet)
&lt;/h3&gt;

&lt;p&gt;I am not adding ads right now. The site is new, and I would rather people use it and trust it first. When ads come, I will update the About page and Privacy Policy to say so clearly. The privacy claim about the tools themselves will not change, because the architecture will not change: your files and numbers still never leave the browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  A question for you
&lt;/h3&gt;

&lt;p&gt;If you build or use client-side tools: &lt;strong&gt;what is the one small annoyance you wish a browser-based tool would just solve, without asking for an account?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For me, it was tax calculators that hide the answer. I suspect everyone has their own version of that ritual.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>privacy</category>
      <category>javascript</category>
      <category>astro</category>
    </item>
  </channel>
</rss>
