<?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: We Luv Tools</title>
    <description>The latest articles on DEV Community by We Luv Tools (@weluvtools).</description>
    <link>https://dev.to/weluvtools</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%2F4044585%2F8ed2a821-a45e-4997-802a-7eb9acbe16fc.webp</url>
      <title>DEV Community: We Luv Tools</title>
      <link>https://dev.to/weluvtools</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/weluvtools"/>
    <language>en</language>
    <item>
      <title>How I Built a Fast, Serverless Online Image Compressor Using TypeScript and Wasm</title>
      <dc:creator>We Luv Tools</dc:creator>
      <pubDate>Mon, 03 Aug 2026 18:52:49 +0000</pubDate>
      <link>https://dev.to/weluvtools/how-i-built-a-fast-serverless-online-image-compressor-using-typescript-and-wasm-4pj</link>
      <guid>https://dev.to/weluvtools/how-i-built-a-fast-serverless-online-image-compressor-using-typescript-and-wasm-4pj</guid>
      <description>&lt;p&gt;Hey DEV Community! 👋&lt;/p&gt;

&lt;p&gt;I recently built an all-in-one image compression tool for &lt;a href="https://weluvtools.com" rel="noopener noreferrer"&gt;WeLuvTools&lt;/a&gt; designed to compress images instantly right in your browser without ever sending files to a backend server. &lt;/p&gt;

&lt;p&gt;Here is a quick look at why and how I built it using &lt;strong&gt;TypeScript, WebAssembly (Wasm), and Web Workers&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  💡 Why Build a Client-Side Compressor?
&lt;/h3&gt;

&lt;p&gt;Most online image compressors require uploading files to a server, processing them, and downloading them back. This approach has a few drawbacks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Privacy Concerns:&lt;/strong&gt; Users have to trust servers with their personal or sensitive images.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network Overhead:&lt;/strong&gt; Uploading large files takes bandwidth and time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server Costs:&lt;/strong&gt; Running heavy image-processing algorithms on a backend gets expensive quickly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By leveraging modern web APIs, we can offload 100% of the image processing directly to the user's browser.&lt;/p&gt;




&lt;h3&gt;
  
  
  🛠️ The Tech Stack &amp;amp; Architecture
&lt;/h3&gt;

&lt;p&gt;Here is how the underlying architecture works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript:&lt;/strong&gt; Keeps the UI state clean, type-safe, and predictable across different image formats and options.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebAssembly (Wasm):&lt;/strong&gt; Handles high-performance image encoding/decoding algorithms (like MozJPEG, WebP, PNGQuant) compiled directly to native-like speeds in the browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Workers:&lt;/strong&gt; Prevents UI freezing! Heavy image compression tasks are offloaded to background threads so the UI stays smooth at 60 FPS while processing large images.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  ⚡ Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🔒 &lt;strong&gt;100% Private:&lt;/strong&gt; Zero server uploads—your images never leave your browser.&lt;/li&gt;
&lt;li&gt;⚡ &lt;strong&gt;Blazing Fast:&lt;/strong&gt; Powered by Web Workers and Wasm for multithreaded speed.&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;Batch Compression:&lt;/strong&gt; Compress multiple images simultaneously.&lt;/li&gt;
&lt;li&gt;🎨 &lt;strong&gt;Format Flexibility:&lt;/strong&gt; Convert and compress JPEG, PNG, WebP, and more on the fly.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🔗 Try It Out
&lt;/h3&gt;

&lt;p&gt;Check out the compressor live on &lt;a href="https://weluvtools.com/tools/all-in-one-image" rel="noopener noreferrer"&gt;WeLuvTools&lt;/a&gt; and let me know what you think! &lt;/p&gt;

&lt;p&gt;I’d love to hear your thoughts on client-side media processing or any feature suggestions you might have. Feel free to ask any technical questions in the comments! 👇&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>typescript</category>
      <category>showdev</category>
    </item>
    <item>
      <title>How to Compress Images in the Browser Without Uploading Them</title>
      <dc:creator>We Luv Tools</dc:creator>
      <pubDate>Thu, 23 Jul 2026 23:39:44 +0000</pubDate>
      <link>https://dev.to/weluvtools/how-to-compress-images-in-the-browser-without-uploading-them-19hm</link>
      <guid>https://dev.to/weluvtools/how-to-compress-images-in-the-browser-without-uploading-them-19hm</guid>
      <description>&lt;p&gt;&lt;a href="https://weluvtools.com/tools/image-compressor" rel="noopener noreferrer"&gt;Image compression&lt;/a&gt; is usually treated as a server-side task. You select an image, upload it to a website, wait for processing, and download the result.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa83p0kwqzhqh7wk866aa.webp" 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%2Fa83p0kwqzhqh7wk866aa.webp" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
That workflow is convenient, but it is not always ideal for private screenshots, documents, personal photos, or business assets.&lt;br&gt;
A better approach is client-side image compression: the browser reads, processes, and exports the image locally without sending it to a server.&lt;br&gt;
&lt;strong&gt;What Is Client-Side Image Compression?&lt;/strong&gt;&lt;br&gt;
Client-side image compression means the image is processed using browser APIs such as:&lt;br&gt;
File&lt;br&gt;
FileReader&lt;br&gt;
createImageBitmap&lt;br&gt;
HTMLCanvasElement&lt;br&gt;
canvas.toBlob()&lt;br&gt;
The basic flow is:&lt;br&gt;
Select image&lt;br&gt;
   ↓&lt;br&gt;
Decode image in browser&lt;br&gt;
   ↓&lt;br&gt;
Draw image to canvas&lt;br&gt;
   ↓&lt;br&gt;
Export with selected quality&lt;br&gt;
   ↓&lt;br&gt;
Download compressed file&lt;br&gt;
The original image does not need to leave the device.&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="nx"&gt;Basic&lt;/span&gt; &lt;span class="nx"&gt;Browser&lt;/span&gt; &lt;span class="nx"&gt;Example&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;compressImage&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="nx"&gt;quality&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&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="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;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="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;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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&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="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;context&lt;/span&gt;&lt;span class="p"&gt;)&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;Canvas is not supported&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;context&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&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="nf"&gt;toBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;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;blob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&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;Compression failed&lt;/span&gt;&lt;span class="dl"&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;}&lt;/span&gt;

        &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/webp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;quality&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;The quality value normally ranges from 0 to 1.&lt;br&gt;
For photographs, values between 0.75 and 0.9 are often a practical starting point. Lower values create smaller files but can introduce visible artifacts.&lt;br&gt;
&lt;strong&gt;WebP, JPEG, and PNG&lt;/strong&gt; Behave Differently&lt;br&gt;
Compression results depend on the image format.&lt;br&gt;
&lt;strong&gt;JPEG&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;JPEG&lt;/strong&gt; is useful for photographs and complex images. It uses lossy compression, so a lower quality setting produces a smaller file but may reduce visual quality.&lt;br&gt;
&lt;strong&gt;PNG&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;PNG&lt;/strong&gt; is useful for logos, illustrations, and images with transparency. Converting a transparent &lt;strong&gt;PNG&lt;/strong&gt; directly to &lt;strong&gt;JPEG&lt;/strong&gt; removes transparency because** JPEG** does not support alpha channels.&lt;br&gt;
&lt;strong&gt;WebP&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;WebP&lt;/strong&gt; is useful when you want smaller files with good visual quality. It supports lossy and lossless compression, transparency, and modern browser workflows.&lt;br&gt;
Why Browser-Only Compression Is Useful&lt;br&gt;
Local processing has several advantages:&lt;br&gt;
&lt;strong&gt;Privacy&lt;/strong&gt;: The selected image does not need to be uploaded.&lt;br&gt;
&lt;strong&gt;Speed&lt;/strong&gt;: There is no upload or server-processing wait.&lt;br&gt;
&lt;strong&gt;Lower infrastructure cost&lt;/strong&gt;: Compression does not require a file-processing server.&lt;br&gt;
&lt;strong&gt;Offline potential&lt;/strong&gt;: A properly cached web application can process supported formats without an internet connection.&lt;br&gt;
&lt;strong&gt;Better control&lt;/strong&gt;: Users can choose quality, format, dimensions, and target size.&lt;br&gt;
However, browser-only processing still depends on the device’s memory and browser support. Very large images may require resizing before compression.&lt;br&gt;
&lt;strong&gt;A Practical Tool&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://weluvtools.com&lt;br&gt;%0A![%20](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/pm3yt40b10dpgitiruj6.webp)" rel="noopener noreferrer"&gt;WeLuvTools&lt;/a&gt; provides a browser-based Image Compressor for compressing JPG, PNG, and &lt;a href="https://weluvtools.com/tools/webp-converter" rel="noopener noreferrer"&gt;WebP images&lt;/a&gt; without requiring an account.&lt;br&gt;
For workflows involving compression, conversion, and resizing together, the &lt;a href="https://weluvtools.com/tools/all-in-one-image" rel="noopener noreferrer"&gt;All-in-One Image Tool&lt;/a&gt; is more suitable.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>beginners</category>
      <category>javascript</category>
      <category>software</category>
    </item>
  </channel>
</rss>
