<?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>Forem: Wuyn Tiny</title>
    <description>The latest articles on Forem by Wuyn Tiny (@nsanglavin).</description>
    <link>https://forem.com/nsanglavin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3831374%2Fb6e04c70-71cd-49d0-a001-4816854b3f5e.png</url>
      <title>Forem: Wuyn Tiny</title>
      <link>https://forem.com/nsanglavin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/nsanglavin"/>
    <language>en</language>
    <item>
      <title>I Built a Free PNG to WebP Converter Using Only Frontend — Here’s What I Learned</title>
      <dc:creator>Wuyn Tiny</dc:creator>
      <pubDate>Sun, 22 Mar 2026 04:37:10 +0000</pubDate>
      <link>https://forem.com/nsanglavin/i-built-a-free-png-to-webp-converter-using-only-frontend-heres-what-i-learned-21dc</link>
      <guid>https://forem.com/nsanglavin/i-built-a-free-png-to-webp-converter-using-only-frontend-heres-what-i-learned-21dc</guid>
      <description>&lt;p&gt;&lt;strong&gt;🚀 Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I recently built a simple online tool to convert PNG images to WebP — and I challenged myself to do it using &lt;strong&gt;only frontend technologies.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No backend. No file uploads to a server.&lt;/p&gt;

&lt;p&gt;At first, it sounded easy… but there were a few interesting challenges along the way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🤔 Why I Built This&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you've worked with images on the web, you probably know this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PNG files are large&lt;/li&gt;
&lt;li&gt;They slow down websites&lt;/li&gt;
&lt;li&gt;Page speed affects SEO&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;WebP solves most of these problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smaller file size&lt;/li&gt;
&lt;li&gt;Good quality&lt;/li&gt;
&lt;li&gt;Supported by modern browsers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I thought:&lt;br&gt;
👉 Why not build a simple tool that converts PNG to WebP instantly?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚙️ Tech Stack&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I kept things simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend: Next.js (CSR)&lt;/li&gt;
&lt;li&gt;Image processing: HTMLCanvas API&lt;/li&gt;
&lt;li&gt;No backend at all&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let the browser handle everything.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;🧠 How It Works&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The core idea is surprisingly straightforward.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User uploads a PNG image&lt;/li&gt;
&lt;li&gt;Load it into an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element&lt;/li&gt;
&lt;li&gt;Draw it onto a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Export it as WebP&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s a simplified version:&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;convertToWebP&lt;/span&gt; &lt;span class="o"&gt;=&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="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;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="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="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;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&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="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;img&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="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;img&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="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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;webp&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/webp&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;webp&lt;/span&gt;&lt;span class="p"&gt;);&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;⚠️ Challenges I Faced&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;1. Memory Issues&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Large images can easily crash the tab or freeze the UI.&lt;/p&gt;

&lt;p&gt;👉 Solution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limit file size&lt;/li&gt;
&lt;li&gt;Resize before processing
&lt;strong&gt;2. Performance&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Processing happens on the main thread → UI lag.&lt;/p&gt;

&lt;p&gt;👉 Solution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Show loading state&lt;/li&gt;
&lt;li&gt;Consider Web Workers (future improvement)
&lt;strong&gt;3. Quality vs Size&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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/webp&lt;/span&gt;&lt;span class="dl"&gt;"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;0.8 = good balance&lt;/li&gt;
&lt;li&gt;Lower → smaller file&lt;/li&gt;
&lt;li&gt;Higher → better quality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🔍 Why No Backend?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I intentionally avoided using a backend because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster (no upload time)&lt;/li&gt;
&lt;li&gt;Better privacy (images stay on device)&lt;/li&gt;
&lt;li&gt;Lower cost (no server needed)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But there are trade-offs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limited by browser performance&lt;/li&gt;
&lt;li&gt;Not ideal for large files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🌐 Live Tool&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you want to try it, here’s the tool I built:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://toolavin.com/png-to-webp" rel="noopener noreferrer"&gt;https://toolavin.com/png-to-webp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s free, no signup required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📊 What I Learned&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The browser is more powerful than we think&lt;/li&gt;
&lt;li&gt;Simple tools can still bring real value&lt;/li&gt;
&lt;li&gt;Performance matters more than features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Building small tools is a great way to learn and experiment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;💡 What’s Next?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’m planning to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add batch processing&lt;/li&gt;
&lt;li&gt;Support more formats&lt;/li&gt;
&lt;li&gt;Improve performance with Web Workers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🙌 Final Thoughts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This was a small project, but I learned a lot from it.&lt;/p&gt;

&lt;p&gt;If you're thinking about building a tool:&lt;br&gt;
👉 Just start simple.&lt;/p&gt;

&lt;p&gt;You don’t need a complex backend to create something useful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💬 Feedback Welcome&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Would love to hear your thoughts or suggestions 🙌&lt;br&gt;
What would you improve?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>nextjs</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
