<?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: Gobinda Adhikari</title>
    <description>The latest articles on DEV Community by Gobinda Adhikari (@gobinda_adhikari_621a159a).</description>
    <link>https://dev.to/gobinda_adhikari_621a159a</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%2F4001909%2F5a5e44cc-2fb8-414e-a0c7-13b4aac89341.jpg</url>
      <title>DEV Community: Gobinda Adhikari</title>
      <link>https://dev.to/gobinda_adhikari_621a159a</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gobinda_adhikari_621a159a"/>
    <language>en</language>
    <item>
      <title>Cropping images into shapes in the browser: canvas clip paths vs SVG masks</title>
      <dc:creator>Gobinda Adhikari</dc:creator>
      <pubDate>Sun, 27 Sep 2026 13:58:02 +0000</pubDate>
      <link>https://dev.to/gobinda_adhikari_621a159a/cropping-images-into-shapes-in-the-browser-canvas-clip-paths-vs-svg-masks-1620</link>
      <guid>https://dev.to/gobinda_adhikari_621a159a/cropping-images-into-shapes-in-the-browser-canvas-clip-paths-vs-svg-masks-1620</guid>
      <description>&lt;h1&gt;
  
  
  Cropping images into shapes in the browser: canvas clip paths vs SVG masks
&lt;/h1&gt;

&lt;p&gt;I build free browser tools, and one problem keeps coming up: letting users crop a photo into a circle, heart, star, or any custom shape — entirely client-side, no upload, no server. There are two solid ways to do it. Here's how they compare, with code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 1: Canvas &lt;code&gt;clip()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The canvas 2D API has clipping built in. Draw your shape path, call &lt;code&gt;clip()&lt;/code&gt;, then draw the image — everything outside the path is discarded:&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;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="s1"&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="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// define the shape (a heart, a star, whatever)&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;beginPath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;drawHeartPath&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="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="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="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;clip&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// the image is now masked to the shape&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;canvas&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="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;png&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="s1"&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="c1"&gt;// transparent outside the shape&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pros: one API, fast, and &lt;code&gt;toDataURL&lt;/code&gt; / &lt;code&gt;toBlob&lt;/code&gt; gives you a transparent PNG for free. Cons: you're limited to what you can express as a canvas path, and anti-aliasing at the edge depends on the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 2: SVG &lt;code&gt;&amp;lt;mask&amp;gt;&lt;/code&gt; / &lt;code&gt;&amp;lt;clipPath&amp;gt;&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If your shapes are defined as SVG (which scales cleanly), apply them as a mask over an &lt;code&gt;&amp;lt;image&amp;gt;&lt;/code&gt; element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 400 400"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;defs&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;clipPath&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"star-clip"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M200,20 L240,150 L380,150 L265,230 L305,360 L200,280 L95,360 L135,230 L20,150 L160,150 Z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/clipPath&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/defs&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;image&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"photo.jpg"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"400"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"400"&lt;/span&gt; &lt;span class="na"&gt;clip-path=&lt;/span&gt;&lt;span class="s"&gt;"url(#star-clip)"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pros: resolution-independent, easy to offer 50+ shapes as reusable path definitions, and CSS can animate the mask. Cons: exporting the result as a standalone PNG still means round-tripping through canvas (&lt;code&gt;drawImage&lt;/code&gt; of the SVG, then &lt;code&gt;toBlob&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one I shipped
&lt;/h2&gt;

&lt;p&gt;I use both: SVG paths as the shape library (easy to add new shapes, crisp at any size), canvas as the export engine (reliable transparent PNG output). The whole thing runs in the page — images never leave the user's device, so there's no upload wait and no privacy question.&lt;/p&gt;

&lt;p&gt;It's live as a free tool if you want to see the result: &lt;a href="https://changeimageshape.com" rel="noopener noreferrer"&gt;Change Image Shape&lt;/a&gt; — pick a photo, choose from 50+ shapes, download. The &lt;a href="https://changeimageshape.com/editor" rel="noopener noreferrer"&gt;editor&lt;/a&gt; is the same pipeline described above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;For simple masking with export, canvas &lt;code&gt;clip()&lt;/code&gt; is the shortest path.&lt;/li&gt;
&lt;li&gt;For a shape &lt;em&gt;library&lt;/em&gt;, keep shapes as SVG path data and render/export via canvas.&lt;/li&gt;
&lt;li&gt;Client-side processing removes an entire class of problems: no storage, no bandwidth bills, no GDPR headache for user photos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've done shape masking differently — OffscreenCanvas, WebGL shaders — I'd like to hear how it went.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>canvas</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
