<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Software Development – IMG.LY Blog</title><description>Posts tagged Software Development on the IMG.LY blog.</description><link>https://img.ly/blog/tag/software-development/</link><language>en-us</language><image><url>https://img.ly/apple-touch-icon.png</url><title>Software Development – IMG.LY Blog</title><link>https://img.ly/blog/tag/software-development/</link></image><atom:link href="https://img.ly/blog/tag/software-development/rss.xml" rel="self" type="application/rss+xml"/><generator>Astro</generator><lastBuildDate>Fri, 19 Jun 2026 11:26:05 GMT</lastBuildDate><ttl>60</ttl><item><title>How to Apply Custom Image Filters Using HTML5 Canvas in JavaScript</title><link>https://img.ly/blog/how-to-apply-filters-in-javascript/</link><guid isPermaLink="true">https://img.ly/blog/how-to-apply-filters-in-javascript/</guid><description>Apply custom filters in JavaScript without external libraries – with the &lt;canvas&gt; element! </description><pubDate>Tue, 14 Sep 2021 15:25:56 GMT</pubDate><content:encoded>&lt;p&gt;In this article, you will learn how to apply custom filters to an image in JavaScript, without using any external library. As you are about to learn, this goal can be achieved by employing only the HTML5 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API&quot;&gt;&lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt;&lt;/a&gt; element.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.instagram.com/&quot;&gt;Instagram&lt;/a&gt; has changed image processing forever. Since its market debut, users have become increasingly interested and accustomed to applying filters to their images and photos. This desirable, valuable, and requested feature might seem complex to implement, but it is not.&lt;/p&gt;
&lt;p&gt;So, let’s see how to implement a feature for users to apply custom filters to an image with Vanilla JavaScript. Follow this step-by-step guide to achieve the following &lt;a href=&quot;https://codesandbox.io/s/blog-how-to-filter-an-image-in-javascript-demo-forked-j3mel?from-embed&quot;&gt;result&lt;/a&gt;:&lt;/p&gt;
&lt;h2 id=&quot;custom-filters-with-canvas&quot;&gt;Custom Filters With &lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The HTML &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API&quot;&gt;&lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt;&lt;/a&gt; element allows you to edit an image. It natively offers you the possibility to implement several image-processing features, such as &lt;a href=&quot;https://img.ly/blog/how-to-draw-on-an-image-with-javascript/&quot;&gt;implementing a brush&lt;/a&gt;. In other words, it is the only prerequisite required to apply custom filters to an image. All you need to do is load an image, turn it into an array of pixels by using the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/getImageData&quot;&gt;&lt;code&gt;getImageData()&lt;/code&gt;&lt;/a&gt; function, and apply a transformation to change their value according to the result you want to achieve. Finally, you can either overwrite the original image or create a new one after applying the filter.&lt;/p&gt;
&lt;p&gt;Now, let’s see everything required to implement such a cool feature.&lt;/p&gt;
&lt;p&gt;Clone the &lt;a href=&quot;https://github.com/Tonel/how-to-apply-custom-image-filters-in-javascript-img-ly&quot;&gt;GitHub repository supporting this tutorial&lt;/a&gt; by launching the following command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    git&lt;/span&gt;&lt;span&gt; clone&lt;/span&gt;&lt;span&gt; https://github.com/Tonel/how-to-apply-custom-image-filters-in-javascript-img-ly&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And try the application by opening the &lt;code&gt;index.html&lt;/code&gt; file in your browser.&lt;/p&gt;
&lt;p&gt;Otherwise, continue following this article to learn how to implement the same demo application step by step.&lt;/p&gt;
&lt;h3 id=&quot;1-implementing-simple-filters&quot;&gt;1. Implementing Simple Filters&lt;/h3&gt;
&lt;p&gt;Simple filters can be applied by changing the value of each pixel the image consists of. For example, you can implement a function to apply &lt;a href=&quot;https://en.wikipedia.org/wiki/Thresholding%5F(image_processing)&quot;&gt;thresholding&lt;/a&gt; as follows:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; applyThreshold&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sourceImageData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;threshold&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 127&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; sourceImageData.data;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; i &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;; i &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; src.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;; i &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; r&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; src[i];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; g&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; src[i &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; b&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; src[i &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // thresholding the current value&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; v&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 0.2126&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; r &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 0.7152&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; g &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 0.0722&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; b &lt;/span&gt;&lt;span&gt;&gt;=&lt;/span&gt;&lt;span&gt; threshold &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; 255&lt;/span&gt;&lt;span&gt; :&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    src[i] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; src[i &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; src[i &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; v;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; sourceImageData;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simple function receives the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ImageData/ImageData&quot;&gt;&lt;code&gt;ImageData&lt;/code&gt;&lt;/a&gt; object relative to the image to be thresholded as a parameter. Then, its &lt;code&gt;data&lt;/code&gt; property representing a one-dimensional array with the image data in the RGBA order is accessed. This contains integer values between &lt;code&gt;0&lt;/code&gt; (black) and &lt;code&gt;255&lt;/code&gt; (white). Finally, &lt;code&gt;data&lt;/code&gt; is iterated and modified to apply the thresholding operation, before returning the updated &lt;code&gt;ImageData&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;That is just an example, but with this approach, you may implement many other simple filters.&lt;/p&gt;
&lt;h3 id=&quot;2-implementing-convolution-based-filters&quot;&gt;2. Implementing Convolution-Based Filters&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Convolution&quot;&gt;Convolution&lt;/a&gt; is one of the most important concepts when it comes to image processing. Particularly, it allows you to apply a filter by taking the weighted sum of a rectangle of pixels from an image and use it as the output value pixel by pixel. The coefficients used to perform the weighted sum are defined through a matrix, which is called &lt;a href=&quot;https://en.wikipedia.org/wiki/Kernel%5F(image_processing)&quot;&gt;&lt;em&gt;kernel&lt;/em&gt;&lt;/a&gt;. By changing the kernel, the final result will change accordingly. Some kernels are more popular than others and can be used for blurring, sharpening, and edge detection. You can find a list of these well-known kernels &lt;a href=&quot;https://en.wikipedia.org/wiki/Kernel%5F(image_processing)#Details&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; applyConvolution&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sourceImageData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;outputImageData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;kernel&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; sourceImageData.data;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; dst&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; outputImageData.data;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; srcWidth&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; sourceImageData.width;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; srcHeight&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; sourceImageData.height;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; side&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Math.&lt;/span&gt;&lt;span&gt;round&lt;/span&gt;&lt;span&gt;(Math.&lt;/span&gt;&lt;span&gt;sqrt&lt;/span&gt;&lt;span&gt;(kernel.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; halfSide&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Math.&lt;/span&gt;&lt;span&gt;floor&lt;/span&gt;&lt;span&gt;(side &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // padding the output by the convolution kernel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; w&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; srcWidth;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; h&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; srcHeight;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // iterating through the output image pixels&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; y &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;; y &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; h; y&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;; x &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; w; x&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      let&lt;/span&gt;&lt;span&gt; r &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        g &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        b &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        a &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      // calculating the weighed sum of the source image pixels that&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      // fall under the convolution kernel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; cy &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;; cy &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; side; cy&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; cx &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;; cx &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; side; cx&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          const&lt;/span&gt;&lt;span&gt; scy&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; y &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; cy &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; halfSide;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          const&lt;/span&gt;&lt;span&gt; scx&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; x &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; cx &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; halfSide;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          if&lt;/span&gt;&lt;span&gt; (scy &lt;/span&gt;&lt;span&gt;&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; scy &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; srcHeight &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; scx &lt;/span&gt;&lt;span&gt;&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; scx &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; srcWidth) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            let&lt;/span&gt;&lt;span&gt; srcOffset &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (scy &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; srcWidth &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; scx) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            let&lt;/span&gt;&lt;span&gt; wt &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; kernel[cy &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; side &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; cx];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            r &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; src[srcOffset] &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; wt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            g &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; src[srcOffset &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; wt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            b &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; src[srcOffset &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; wt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            a &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; src[srcOffset &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; wt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; dstOffset&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (y &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; w &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; x) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      dst[dstOffset] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; r;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      dst[dstOffset &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; g;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      dst[dstOffset &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; b;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      dst[dstOffset &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; a;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; outputImageData;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function expects the source &lt;code&gt;ImageData&lt;/code&gt; object as a parameter. Similarly, you must provide it with a blank &lt;code&gt;ImageData&lt;/code&gt; object with the same size as the first one to be used as output, and a matrix representing the kernel. Then, the convolution algorithm is applied on the &lt;code&gt;outputImageData&lt;/code&gt; object by iterating on the &lt;code&gt;sourceImageData&lt;/code&gt; one.&lt;/p&gt;
&lt;p&gt;Et voilà! Now, you have the building blocks required to implement a filtering feature in Vanilla JavaScript. Let’s see how in detail.&lt;/p&gt;
&lt;h3 id=&quot;3-filtering-in-action&quot;&gt;3. Filtering in Action&lt;/h3&gt;
&lt;p&gt;Let’s take a look at how to implement a simple application allowing users to upload an image, choose the filter to apply, and see the final result :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;!&lt;/span&gt;&lt;span&gt;DOCTYPE&lt;/span&gt;&lt;span&gt; html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;#x3C;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;Image Resizer&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      Please, upload an image and apply the filter that best suits the image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;upload&quot;&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;file&quot;&lt;/span&gt;&lt;span&gt; accept&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;image/*&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;images&quot;&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;visibility: hidden;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;Original image&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;imageToFilter&quot;&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;max-width: 100%; height: 200px;&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;margin-top: 5px;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;Filter: &amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;radio&quot;&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;filterRadio&quot;&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;noFilter&quot;&lt;/span&gt;&lt;span&gt; checked&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&gt;No Filter&amp;#x3C;/&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;radio&quot;&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;filterRadio&quot;&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;threshold&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&gt;Threshold&amp;#x3C;/&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;radio&quot;&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;filterRadio&quot;&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;sharpen&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&gt;Sharpen&amp;#x3C;/&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;radio&quot;&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;filterRadio&quot;&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;blur&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&gt;Blur&amp;#x3C;/&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;Filtered image&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;filteredImage&quot;&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;max-width: 100%;&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;src/index.js&quot;&lt;/span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// hiding the div that will contain the images&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; imagesDiv&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;#images&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; fileInput&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;#upload&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; imageToFilter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; filteredImage&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;#filteredImage&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// initializing the filter value&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; filterElement&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;getElementsByName&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;filterRadio&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; filter;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filterElement.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (f.checked) filter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; f.value;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// applying the selected filter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filterElement.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  f.&lt;/span&gt;&lt;span&gt;onclick&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    filter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; f.value;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    filteredImage.src &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; filterImage&lt;/span&gt;&lt;span&gt;(imageToFilter, filter);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fileInput.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;change&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; fileInput.files;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // displaying the uploaded image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  imageToFilter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;#imageToFilter&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  imageToFilter.src &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; fileToDataUri&lt;/span&gt;&lt;span&gt;(file);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // making the div containing the image visible&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  imagesDiv.style.visibility &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;visible&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // applying the defaul filter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  imageToFilter.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;load&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    filteredImage.src &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; filterImage&lt;/span&gt;&lt;span&gt;(imageToFilter, filter);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; fileToDataUri&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Promise&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; reader&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; FileReader&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    reader.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;load&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      resolve&lt;/span&gt;&lt;span&gt;(reader.result);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    reader.&lt;/span&gt;&lt;span&gt;readAsDataURL&lt;/span&gt;&lt;span&gt;(field);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; filterImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;imageToFilter&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; canvas&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;createElement&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;canvas&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; context&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; canvas.&lt;/span&gt;&lt;span&gt;getContext&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;2d&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; canvasWidth&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; imageToFilter.width;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; canvasHeight&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; imageToFilter.height;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  canvas.width &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; canvasWidth;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  canvas.height &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; canvasHeight;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  context.&lt;/span&gt;&lt;span&gt;drawImage&lt;/span&gt;&lt;span&gt;(imageToFilter, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, canvasWidth, canvasHeight);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; sourceImageData&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; context.&lt;/span&gt;&lt;span&gt;getImageData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, canvasWidth, canvasHeight);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; blankOutputImageData&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; context.&lt;/span&gt;&lt;span&gt;createImageData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    canvasWidth,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    canvasHeight&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; outputImageData&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; applyFilter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    sourceImageData,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    blankOutputImageData,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    filter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  context.&lt;/span&gt;&lt;span&gt;putImageData&lt;/span&gt;&lt;span&gt;(outputImageData, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; canvas.&lt;/span&gt;&lt;span&gt;toDataURL&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; applyFilter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sourceImageData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;outputImageData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (filter &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &apos;noFilter&apos;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; sourceImageData;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; if&lt;/span&gt;&lt;span&gt; (filter &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &apos;threshold&apos;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; applyThreshold&lt;/span&gt;&lt;span&gt;(sourceImageData);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; if&lt;/span&gt;&lt;span&gt; (filter &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &apos;sharpen&apos;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; applyConvolution&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      sourceImageData,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      outputImageData,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      [&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; if&lt;/span&gt;&lt;span&gt; (filter &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &apos;blur&apos;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; applyConvolution&lt;/span&gt;&lt;span&gt;(sourceImageData, outputImageData, [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      1&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 16&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      2&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 16&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      1&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 16&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      2&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 16&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      4&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 16&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      2&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 16&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      1&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 16&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      2&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 16&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      1&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 16&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // fallback option&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; sourceImageData;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; applyThreshold&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sourceImageData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;threshold&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 127&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; sourceImageData.data;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; i &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;; i &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; src.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;; i &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; r&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; src[i];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; g&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; src[i &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; b&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; src[i &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // thresholding the current value&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; v&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 0.2126&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; r &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 0.7152&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; g &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 0.0722&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; b &lt;/span&gt;&lt;span&gt;&gt;=&lt;/span&gt;&lt;span&gt; threshold &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; 255&lt;/span&gt;&lt;span&gt; :&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    src[i] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; src[i &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; src[i &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; v;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; sourceImageData;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; applyConvolution&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sourceImageData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;outputImageData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;kernel&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; sourceImageData.data;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; dst&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; outputImageData.data;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; srcWidth&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; sourceImageData.width;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; srcHeight&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; sourceImageData.height;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; side&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Math.&lt;/span&gt;&lt;span&gt;round&lt;/span&gt;&lt;span&gt;(Math.&lt;/span&gt;&lt;span&gt;sqrt&lt;/span&gt;&lt;span&gt;(kernel.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; halfSide&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Math.&lt;/span&gt;&lt;span&gt;floor&lt;/span&gt;&lt;span&gt;(side &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // padding the output by the convolution kernel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; w&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; srcWidth;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; h&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; srcHeight;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // iterating through the output image pixels&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; y &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;; y &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; h; y&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;; x &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; w; x&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      let&lt;/span&gt;&lt;span&gt; r &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        g &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        b &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        a &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      // calculating the weighed sum of the source image pixels that&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      // fall under the convolution kernel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; cy &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;; cy &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; side; cy&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; cx &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;; cx &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; side; cx&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          const&lt;/span&gt;&lt;span&gt; scy&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; y &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; cy &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; halfSide;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          const&lt;/span&gt;&lt;span&gt; scx&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; x &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; cx &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; halfSide;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          if&lt;/span&gt;&lt;span&gt; (scy &lt;/span&gt;&lt;span&gt;&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; scy &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; srcHeight &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; scx &lt;/span&gt;&lt;span&gt;&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; scx &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; srcWidth) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            let&lt;/span&gt;&lt;span&gt; srcOffset &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (scy &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; srcWidth &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; scx) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            let&lt;/span&gt;&lt;span&gt; wt &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; kernel[cy &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; side &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; cx];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            r &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; src[srcOffset] &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; wt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            g &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; src[srcOffset &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; wt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            b &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; src[srcOffset &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; wt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            a &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; src[srcOffset &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; wt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; dstOffset&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (y &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; w &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; x) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      dst[dstOffset] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; r;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      dst[dstOffset &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; g;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      dst[dstOffset &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; b;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      dst[dstOffset &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; a;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; outputImageData;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the HTML page contains everything required to upload an image, and select a filter. There is also a no filter option, allowing users to restore the original image. Also, to better appreciate the effects of each filter, the original image is always shown as well.&lt;/p&gt;
&lt;p&gt;From a technical point of view, what happens is that the &lt;code&gt;input&lt;/code&gt; element is used to upload an optional image. Then, the uploaded image is passed to the &lt;code&gt;applyFilter()&lt;/code&gt; function, which takes care of applying the &lt;code&gt;filter&lt;/code&gt;selected in the HTML radio buttons by calling the right function. This can involve either a simple filter, or a convolution-based one. In this latter case, the appropriate kernel is passed to the &lt;code&gt;applyConvolution()&lt;/code&gt; function as well. Then, the output &lt;code&gt;ImageData&lt;/code&gt; object is used to show the resulting image by using the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData&quot;&gt;&lt;code&gt;putImageData()&lt;/code&gt;&lt;/a&gt; function. Please, note that the example that was just implemented corresponds to the live demo you can find at the beginning of the article.&lt;/p&gt;
&lt;h2 id=&quot;final-considerations&quot;&gt;Final Considerations&lt;/h2&gt;
&lt;p&gt;Implementing image filtering is not a simple task and can easily be implemented with Vanilla JavaScript. On the other hand, the more filters you want to add, the more complicated things can get. In this tutorial, you just saw how to implement non-complex filters, but there are filtering techniques that require much more effort. Plus, the majority of them involve iterating through each pixel. As a result, on large images, this can turn into a time-consuming process. So, if you do not want to ruin the user experience, you might be forced to adapt academic and well-known algorithms to make them more efficient. Thus, implementing this feature efficiently can turn into a considerably complex task.&lt;/p&gt;
&lt;p&gt;If you do not want to deal with such unavoidable optimizations, a commercial and all-in-one solution like &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditorSDK&lt;/a&gt; should be taken into account. This way, you will not have to worry about accomplishing challenging and time-consuming tasks anymore. Also, whenever you need help, you can ask for support from the &lt;a href=&quot;https://img.ly/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;img.ly&lt;/a&gt; developers who built the SDK.&lt;/p&gt;
&lt;h2 id=&quot;filter-an-image-with-photoeditorsdk&quot;&gt;Filter an Image with &lt;code&gt;photoeditorsdk&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;First, you should read &lt;a href=&quot;https://img.ly/docs/pesdk/web/guides/umd/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;this&lt;/a&gt; article from &lt;a href=&quot;https://img.ly/docs/pesdk/guides/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;the official documentation&lt;/a&gt; on how to get started with &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditorSDK&lt;/a&gt; in HTML and JavaScript. Then, by using &lt;a href=&quot;https://img.ly/docs/pesdk/web/features/filters/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;Filters&lt;/a&gt; feature, you can start enjoying more than 60 high-quality filters. Moreover, each of them was optimized natively to be lightning fast. This way, you can minimize the processing time, and achieve the following result with a few clicks:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;html5-canva-image-filter&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1213px) 1213px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1213&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/html5-canva-image-filter_ZMDvDL.webp&quot; srcset=&quot;/_astro/html5-canva-image-filter_Z2cHfQB.webp 640w, /_astro/html5-canva-image-filter_2dP6Nx.webp 750w, /_astro/html5-canva-image-filter_6Vzw0.webp 828w, /_astro/html5-canva-image-filter_9Guoq.webp 1080w, /_astro/html5-canva-image-filter_ZMDvDL.webp 1213w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this article, we looked at how to implement a feature to allows users to apply a filter on an image with JavaScript. Implementing such a feature can be entirely achieved by only using the HTML5 &lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt; element. However, making the image processing fast enough not to make users wait can become a problem. This is the real issue when dealing with such a feature, which inevitably involves iterating through each pixel of which an image is composed. Consequently, optimizing well-known filtering algorithms to avoid this issue is what makes implementing the feature challenging. So, if you do not want to deal with this issue, you can adopt an easy, complete, advanced, and all-in-one solution – such as &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditorSDK&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for reading! We hope that you found this article helpful. Feel free to reach out to us on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt; with any questions, comments, or suggestions.&lt;/p&gt;</content:encoded><dc:creator>Antonello</dc:creator><media:content url="https://blog.img.ly/2021/09/html5-filter-canva-image-filter.jpg" medium="image"/><category>HTML5</category><category>App Development</category><category>Web Development</category><category>Image Editing</category><category>Photo Editing</category><category>Software Development</category><category>How-To</category><category>Tutorial</category></item><item><title>How to Trim and Crop Video in Swift</title><link>https://img.ly/blog/trim-and-crop-video-in-swift/</link><guid isPermaLink="true">https://img.ly/blog/trim-and-crop-video-in-swift/</guid><description>Learn how to use Swift and AVKit to crop a video clip and trim a video timeline. </description><pubDate>Tue, 31 Aug 2021 16:03:25 GMT</pubDate><content:encoded>&lt;p&gt;In this article, you will see how to use Swift and &lt;code&gt;AVKit&lt;/code&gt; to crop a video clip and how to trim a video’s timeline. Then you will learn how to use &lt;code&gt;AVAssetExportSession&lt;/code&gt; to write your edited video to disk. The code in this article uses Swift 5 and Xcode 12.5. Clone &lt;a href=&quot;https://github.com/waltertyree/trim-crop-video&quot;&gt;this repository&lt;/a&gt; for a sample project and example code.&lt;/p&gt;
&lt;h2 id=&quot;anatomy-of-a-video-file&quot;&gt;Anatomy of a Video File&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;AVFoundation&lt;/code&gt; and &lt;code&gt;AVKit&lt;/code&gt; frameworks are what Swift and iOS use to manage audio and video. When starting out, you may use an &lt;code&gt;AVPlayerViewController&lt;/code&gt; for playback with the same controls and features as the native players. You can also use an &lt;code&gt;AVPlayer&lt;/code&gt; object and provide your own playback and editing controls. Whichever you choose, you begin by loading a media file (&lt;code&gt;.mp3&lt;/code&gt;, &lt;code&gt;.mov&lt;/code&gt;, etc.) into an &lt;code&gt;AVPlayerItem&lt;/code&gt;. Inside the &lt;code&gt;AVPlayerItem&lt;/code&gt;, the media becomes an &lt;code&gt;AVAsset&lt;/code&gt; which may have many &lt;code&gt;tracks&lt;/code&gt; of video, audio, text, closed captions etc.&lt;/p&gt;
&lt;p&gt;To manipulate an &lt;code&gt;AVAsset&lt;/code&gt;, Apple provides the &lt;code&gt;AVComposition&lt;/code&gt; class. The &lt;code&gt;AVComposition&lt;/code&gt; can act on a single track or many tracks to filter and mix underlying media and produces a single output. An &lt;code&gt;AVPlayer&lt;/code&gt; shows the output of an &lt;code&gt;AVComposition&lt;/code&gt; on a device. When it is time to export, you can use an &lt;code&gt;AVAssetExportSession&lt;/code&gt; with the same &lt;code&gt;AVComposition&lt;/code&gt; to write to disk or upload to a server.&lt;/p&gt;
&lt;h2 id=&quot;cropping-video-to-a-rectangle&quot;&gt;Cropping Video to a Rectangle&lt;/h2&gt;
&lt;p&gt;Apple provides some different &lt;code&gt;AVComposition&lt;/code&gt; classes optimized for common tasks. Apple recommends using one of these classes instead of writing custom &lt;code&gt;AVComposition&lt;/code&gt; classes whenever possible. The reason for this is that when Apple introduces new technologies (like HDR Video), they will ensure it works with their classes. If you’ve written your own, you will have to update it to work with the new technologies. In this article you will crop the video to a rectangle and let the audio pass through as recorded. A good class to use for this task will be the &lt;code&gt;AVMutableVideoComposition&lt;/code&gt; with the &lt;code&gt;init(asset: AVAsset, applyingCIFiltersWithHandler applier: @escaping (AVAsynchronousCIImageFilteringRequest) -&gt; Void)&lt;/code&gt; initializer. This will allow you to apply a &lt;code&gt;CIFilter&lt;/code&gt; to each frame of the video. Apple provides an efficient cropping filter called &lt;code&gt;CICrop&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;func&lt;/span&gt;&lt;span&gt; transformVideo&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;: AVPlayerItem, &lt;/span&gt;&lt;span&gt;cropRect&lt;/span&gt;&lt;span&gt;: CGRect) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; cropScaleComposition &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVMutableVideoComposition&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;asset&lt;/span&gt;&lt;span&gt;: item.asset, &lt;/span&gt;&lt;span&gt;applyingCIFiltersWithHandler&lt;/span&gt;&lt;span&gt;: {request &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    let&lt;/span&gt;&lt;span&gt; cropFilter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CIFilter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;CICrop&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; //1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    cropFilter.&lt;/span&gt;&lt;span&gt;setValue&lt;/span&gt;&lt;span&gt;(request.sourceImage, &lt;/span&gt;&lt;span&gt;forKey&lt;/span&gt;&lt;span&gt;: kCIInputImageKey) &lt;/span&gt;&lt;span&gt;//2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    cropFilter.&lt;/span&gt;&lt;span&gt;setValue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;CIVector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cgRect&lt;/span&gt;&lt;span&gt;: cropRect), &lt;/span&gt;&lt;span&gt;forKey&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;inputRectangle&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    let&lt;/span&gt;&lt;span&gt; imageAtOrigin &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; cropFilter.outputImage&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;transformed&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;CGAffineTransform&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;translationX&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;cropRect.origin.x, &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;cropRect.origin.y)) &lt;/span&gt;&lt;span&gt;//3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    request.&lt;/span&gt;&lt;span&gt;finish&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt;: imageAtOrigin, &lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;//4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  cropScaleComposition.renderSize &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; cropRect.&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt; //5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  item.videoComposition &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; cropScaleComposition  &lt;/span&gt;&lt;span&gt;//6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;applyingCIFiltersWithHandler&lt;/code&gt; will execute for every frame in the &lt;code&gt;asset&lt;/code&gt; of the &lt;code&gt;AVPlayerItem&lt;/code&gt;. Here is what the code above will do:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a CICrop filter (note that this demo uses &lt;code&gt;!&lt;/code&gt; to force unwrap, production code should handle failure)&lt;/li&gt;
&lt;li&gt;Add the &lt;code&gt;.sourceImage&lt;/code&gt; (a &lt;code&gt;CIImage&lt;/code&gt;) from the request to the filter.&lt;/li&gt;
&lt;li&gt;Move the cropped image to the origin of the video frame. When you resize the frame (step 4) it will resize from the origin.&lt;/li&gt;
&lt;li&gt;Output the transformed frame image&lt;/li&gt;
&lt;li&gt;Set the size of the video frame to the cropped size&lt;/li&gt;
&lt;li&gt;Attach the composition to the &lt;code&gt;videoComposition&lt;/code&gt; property of the &lt;code&gt;asset&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Notice that the transformation does not alter the underlying asset. It creates a &lt;code&gt;videoComposition&lt;/code&gt; filter to attach to the item during playback. After the transformation, &lt;code&gt;AVPlayer&lt;/code&gt; will display the new creation when executing its &lt;code&gt;.play()&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;Sharp eyed readers will have noticed that the &lt;code&gt;init&lt;/code&gt; method mentions “applying &lt;em&gt;CIFilters&lt;/em&gt;” plural. Instead of one filter, you can create an entire pipeline of &lt;code&gt;CIFilter&lt;/code&gt; objects to manipulate the visual properties of the frames. The &lt;code&gt;request&lt;/code&gt; object also contains the &lt;code&gt;compositionTime&lt;/code&gt; so filters can change at different parts of the video.&lt;/p&gt;
&lt;h2 id=&quot;trimming-the-time-of-a-video&quot;&gt;Trimming the Time of a Video&lt;/h2&gt;
&lt;p&gt;When an &lt;code&gt;AVPlayer&lt;/code&gt; loads an &lt;code&gt;AVItem&lt;/code&gt; the start time of the video will be at &lt;code&gt;CMTime.zero&lt;/code&gt;. The end will be the &lt;code&gt;duration&lt;/code&gt; property of the &lt;code&gt;AVItem&lt;/code&gt;. To adjust the playback times, you call the &lt;code&gt;.seek&lt;/code&gt; function to move to the new start time and set the &lt;code&gt;forwardPlaybackEndTime&lt;/code&gt; to the new end time. Now the player will only play the part of the clip between those times.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//The player object is already created and configured to play video in the ViewController&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//load a video .mov file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.player &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVPlayer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;: Bundle.main.&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;forResource&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;grocery-train&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;withExtension&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;mov&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//Set the start time to 5 seconds.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; startTime &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CMTimeMakeWithSeconds&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;preferredTimescale&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;600&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//Convert the duration of the video to seconds&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; videoDurationInSeconds &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; self&lt;/span&gt;&lt;span&gt;.player&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;.currentItem&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;.duration.seconds&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//Subtract 5 seconds from the end time&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; endTime &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CMTimeMakeWithSeconds&lt;/span&gt;&lt;span&gt;(videoDurationInSeconds &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;preferredTimescale&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;600&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//Assign the new values to the start and end time&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.player&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;seek&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;: startTime)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.player&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.currentItem&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.forwardPlaybackEndTime &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; endTime&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//Play the video&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.player&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;play&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;CMTime&lt;/code&gt; (Core Media Time) object uses timescales and &lt;code&gt;Int&lt;/code&gt; values to map to the individual frames of tracks. Video tracks commonly come to your app with 24, 30, 60 or 120 frames per second. Converting between these different formats using Floats or Doubles would be imprecise. When converting seconds to &lt;code&gt;CMTime&lt;/code&gt; with video, &lt;code&gt;600&lt;/code&gt; is the commonly used timescale because it is a common multiple of all of the standard frames per second.&lt;/p&gt;
&lt;h2 id=&quot;exporting-the-video&quot;&gt;Exporting the Video&lt;/h2&gt;
&lt;p&gt;As with the &lt;code&gt;videoComposition&lt;/code&gt; property, setting the start and end times of the player only modifies the playback of the video clip. The underlying asset remains unchanged. When you use the &lt;code&gt;AVAssetExportSession&lt;/code&gt;, the new video file will have the change. The export session applies time range and video composition objects as it writes the file. A function to export might look like:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;func&lt;/span&gt;&lt;span&gt; export&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_&lt;/span&gt;&lt;span&gt; asset: AVAsset, &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt; outputMovieURL: URL, &lt;/span&gt;&lt;span&gt;startTime&lt;/span&gt;&lt;span&gt;: CMTime, &lt;/span&gt;&lt;span&gt;endTime&lt;/span&gt;&lt;span&gt;: CMTime, &lt;/span&gt;&lt;span&gt;composition&lt;/span&gt;&lt;span&gt;: AVVideoComposition) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //Create trim range&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; timeRange &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CMTimeRangeFromTimeToTime&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt;: startTime, &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt;: endTime)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //delete any old file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  do&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    try&lt;/span&gt;&lt;span&gt; FileManager.default.&lt;/span&gt;&lt;span&gt;removeItem&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;at&lt;/span&gt;&lt;span&gt;: outputMovieURL)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Could not remove file &lt;/span&gt;&lt;span&gt;\(error.&lt;/span&gt;&lt;span&gt;localizedDescription&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //create exporter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; exporter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVAssetExportSession&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;asset&lt;/span&gt;&lt;span&gt;: asset, &lt;/span&gt;&lt;span&gt;presetName&lt;/span&gt;&lt;span&gt;: AVAssetExportPresetHighestQuality)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //configure exporter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  exporter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.videoComposition &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; composition&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  exporter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.outputURL &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; outputMovieURL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  exporter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.outputFileType &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; .mov&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  exporter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.timeRange &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; timeRange&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //export!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  exporter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exportAsynchronously&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;completionHandler&lt;/span&gt;&lt;span&gt;: { [&lt;/span&gt;&lt;span&gt;weak&lt;/span&gt;&lt;span&gt; exporter] &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    DispatchQueue.main.&lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      if&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; error &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; exporter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;failed &lt;/span&gt;&lt;span&gt;\(error.&lt;/span&gt;&lt;span&gt;localizedDescription&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Video saved to &lt;/span&gt;&lt;span&gt;\(outputMovieURL)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;going-further&quot;&gt;Going Further&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;AVKit&lt;/code&gt; and &lt;code&gt;AVFoundation&lt;/code&gt; provide simple objects for manipulating video files. The difficulty when working with video and audio tracks usually comes while providing editing controls for a user. The image below shows how the original video dimensions might change from creation to display.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;different screen dimensions of video during different stages of editing&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1453px) 1453px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1453&quot; height=&quot;716&quot; src=&quot;https://img.ly/_astro/resizing_1kxMMH.webp&quot; srcset=&quot;/_astro/resizing_ZD5fcE.webp 640w, /_astro/resizing_w8gz1.webp 750w, /_astro/resizing_160u1K.webp 828w, /_astro/resizing_1mGc6g.webp 1080w, /_astro/resizing_22qr8E.webp 1280w, /_astro/resizing_1kxMMH.webp 1453w&quot;&gt;&lt;/p&gt;
&lt;p&gt;The original 2160x3840 video appears on an iPhone in a 357x635 frame. Additionally the origin point (green dot) of the video file and the origin point of the UIViews are not equal. Passing the frame of the red, cropping rectangle to an &lt;code&gt;AVComposition&lt;/code&gt; would not work as expected. Inside of the &lt;code&gt;AVComposition&lt;/code&gt; the video resumes it’s 720x1280 dimensions while the 250x250 cropping rectangle would remain 250x250.&lt;/p&gt;
&lt;p&gt;Before using a UIView rectangle with an &lt;code&gt;AVComposition&lt;/code&gt; it needs to resize and the origin point needs to align to the video’s origin. Your app must apply a &lt;code&gt;CGAffineTransform&lt;/code&gt; to reorient the origin points.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;flip and slide rectangle&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1209px) 1209px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1209&quot; height=&quot;793&quot; src=&quot;https://img.ly/_astro/transform-rect_28A6Vy.webp&quot; srcset=&quot;/_astro/transform-rect_ZbJbv8.webp 640w, /_astro/transform-rect_Z25Ixcl.webp 750w, /_astro/transform-rect_Z9DQt7.webp 828w, /_astro/transform-rect_VNLLC.webp 1080w, /_astro/transform-rect_28A6Vy.webp 1209w&quot;&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; cropRect &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; self&lt;/span&gt;&lt;span&gt;.croppingView.frame &lt;/span&gt;&lt;span&gt;//1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; originFlipTransform &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CGAffineTransform&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scaleX&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;-1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; frameTranslateTransform &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CGAffineTransform&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;translationX&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;: renderingSize.height)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cropRect &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; cropRect.&lt;/span&gt;&lt;span&gt;applying&lt;/span&gt;&lt;span&gt;(originFlipTransform) &lt;/span&gt;&lt;span&gt;//2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cropRect &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; cropRect.&lt;/span&gt;&lt;span&gt;applying&lt;/span&gt;&lt;span&gt;(frameTranslateTransform) &lt;/span&gt;&lt;span&gt;//3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you apply regular ratio math to the dimensions of the cropping rectangle to match the video dimensions.&lt;br&gt;
&lt;img alt=&quot;resize rectangle to video dimensions&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 359px) 359px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;359&quot; height=&quot;494&quot; src=&quot;https://img.ly/_astro/translate-resize_54OyH.webp&quot; srcset=&quot;/_astro/translate-resize_54OyH.webp 359w&quot;&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; renderingSize &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; playerItem.presentationSize&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; xFactor &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; renderingSize.width &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; playerView.bounds.&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;.width&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; yFactor &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; renderingSize.height &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; playerView.bounds.&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;.height&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; newX &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; croppingView.frame.origin.x &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; xFactor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; newW &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; croppingView.frame.width &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; xFactor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; newY &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; croppingView.frame.origin.y &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; yFactor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; newH &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; croppingView.frame.height &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; yFactor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; cropRect &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CGRect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;: newX, &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;: newY, &lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt;: newW, &lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;: newH)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The transformations above are standard when working with &lt;code&gt;AVKit&lt;/code&gt; and &lt;code&gt;UIKit&lt;/code&gt; or &lt;code&gt;SwiftUI&lt;/code&gt; in an app. The math is not complex, but it is tedious. Unless your app is a custom video editing application, you may find that using a commercial solution such as &lt;a href=&quot;https://img.ly/products/video-sdk/&quot;&gt;VideoEditorSDK&lt;/a&gt; is a better approach. By adding the &lt;a href=&quot;https://img.ly/products/video-sdk/&quot;&gt;VideoEditorSDK&lt;/a&gt;, your app can have professional appearing trim and crop controls as well as filtering, text and more.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;trim crop and other controls&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1131px) 1131px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1131&quot; height=&quot;779&quot; src=&quot;https://img.ly/_astro/crop-trim-and-others_3Iidn.webp&quot; srcset=&quot;/_astro/crop-trim-and-others_Z2jzuUp.webp 640w, /_astro/crop-trim-and-others_1dym89.webp 750w, /_astro/crop-trim-and-others_B0bOy.webp 828w, /_astro/crop-trim-and-others_Z1WfCJ7.webp 1080w, /_astro/crop-trim-and-others_3Iidn.webp 1131w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this article, you saw how to crop a video image and how to trim time from a track, all in Swift. Further, you saw how using an SDK such as &lt;a href=&quot;https://img.ly/products/video-sdk/&quot;&gt;VideoEditorSDK&lt;/a&gt; allows you to provide full-featured video editing for any application.&lt;br&gt;
Looking for more video capabilities? Check out our solution for &lt;a href=&quot;https://img.ly/use-cases/story-reels-short-video-creation/&quot;&gt;Short Video Creation&lt;/a&gt;, and &lt;a href=&quot;https://img.ly/products/video-sdk-mobile/&quot;&gt;Camera SDK&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! We hope that you found this tutorial helpful. Feel free to reach out to us on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt; with any questions, comments, or suggestions.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Walter</dc:creator><media:content url="https://blog.img.ly/2021/08/resize-swift.jpg" medium="image"/><category>Swift</category><category>Software Development</category><category>Mobile App Development</category><category>Video Editing</category><category>Video Editor</category><category>Code</category><category>Developer Tools</category><category>Tutorial</category><category>App Development</category><category>How-To</category></item><item><title>How to Draw on an Image With JavaScript</title><link>https://img.ly/blog/how-to-draw-on-an-image-with-javascript/</link><guid isPermaLink="true">https://img.ly/blog/how-to-draw-on-an-image-with-javascript/</guid><description>Easily implement a brush to draw on an image in JavaScript, without using any external library!</description><pubDate>Mon, 23 Aug 2021 15:20:14 GMT</pubDate><content:encoded>&lt;p&gt;In this article, you will learn to implement a brush to draw on an image in JavaScript, without using any dedicated external libraries, like those of &lt;a href=&quot;https://img.ly/blog/how-to-crop-an-image-in-react-with-react-image-crop/&quot;&gt;React for image manipulations&lt;/a&gt;. As you are going to see, this can be achieved effortlessly using the HTML5 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API&quot;&gt;&lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt;&lt;/a&gt; element.&lt;/p&gt;
&lt;p&gt;In the last few years, touchscreens have become more and more common. This technology has made drawing on screens much easier and accessible to everyone. Thus, allowing users to draw on photos or pictures is a more common feature than ever before. For example, we all expect to be able to modify pictures we took before sending them in messaging applications. This is just one of the several real-world case scenarios where you may need to implement this increasingly valuable feature.&lt;/p&gt;
&lt;p&gt;So, let’s see how to draw on an image with Vanilla JavaScript. Follow this step-by-step guide to achieve the following result:&lt;/p&gt;
&lt;h2 id=&quot;drawing-on-images-with-canvas&quot;&gt;Drawing on Images With &lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Clone the &lt;a href=&quot;https://github.com/Tonel/how-to-draw-on-an-image-with-javascript-img-ly&quot;&gt;GitHub repository that supports this article&lt;/a&gt; by launching the following command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git clone https://github.com/Tonel/how-to-draw-on-an-image-with-javascript-img-ly&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, launch the &lt;code&gt;index.html&lt;/code&gt; page to try the demo application.&lt;/p&gt;
&lt;p&gt;Otherwise, continue following this tutorial to see how to implement the demo application step by step.&lt;/p&gt;
&lt;h3 id=&quot;1-implementing-the-brush-feature&quot;&gt;1. Implementing the Brush Feature&lt;/h3&gt;
&lt;p&gt;The HTML &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API&quot;&gt;&lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt;&lt;/a&gt; element natively offers everything required to implement a brush feature. So, it is actually the only prerequisites. Despite it is typically used for game graphics, animations, real-time video processing, and data visualization, you can also employ it as a tool to draw on images. This powerful tool allows you to implement many cool features, such as resizing an image. Follow &lt;a href=&quot;https://img.ly/blog/how-to-resize-an-image-with-javascript/&quot;&gt;this&lt;/a&gt; link to find out how.&lt;br&gt;
In this case, it takes only a handful of lines of code to implement a brush. Let’s see how together:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; drawOnImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; canvasElement&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;canvas&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; context&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; canvasElement.&lt;/span&gt;&lt;span&gt;getContext&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;2d&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // if an image is present,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // the image passed as a parameter is drawn in the canvas&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (image) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; imageWidth&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; image.width;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; imageHeight&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; image.height;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // rescaling the canvas element&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    canvasElement.width &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; imageWidth;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    canvasElement.height &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; imageHeight;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    context.&lt;/span&gt;&lt;span&gt;drawImage&lt;/span&gt;&lt;span&gt;(image, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, imageWidth, imageHeight);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; isDrawing;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  canvasElement.&lt;/span&gt;&lt;span&gt;onmousedown&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    isDrawing &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    context.&lt;/span&gt;&lt;span&gt;beginPath&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    context.lineWidth &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 10&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    context.strokeStyle &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;black&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    context.lineJoin &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;round&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    context.lineCap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;round&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    context.&lt;/span&gt;&lt;span&gt;moveTo&lt;/span&gt;&lt;span&gt;(e.clientX, e.clientY);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  canvasElement.&lt;/span&gt;&lt;span&gt;onmousemove&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (isDrawing) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      context.&lt;/span&gt;&lt;span&gt;lineTo&lt;/span&gt;&lt;span&gt;(e.clientX, e.clientY);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      context.&lt;/span&gt;&lt;span&gt;stroke&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  canvasElement.&lt;/span&gt;&lt;span&gt;onmouseup&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; function&lt;/span&gt;&lt;span&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    isDrawing &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    context.&lt;/span&gt;&lt;span&gt;closePath&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, a canvas already presents on the HTML page is retrieved. Then, a 2D rendering context is initialized. This will be used to draw on the surface of the blank canvas or on the optional image passed as a parameter. In this last case, the canvas element is resized to fit the image dimensions. Also, the image is drawn by employing the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage&quot;&gt;&lt;code&gt;drawImage()&lt;/code&gt;&lt;/a&gt; function. Next, the logic necessary to draw is implemented.&lt;/p&gt;
&lt;p&gt;On &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element/mousedown_event&quot;&gt;&lt;code&gt;mousedown&lt;/code&gt;&lt;/a&gt;, brush color, thickness, and opacity are defined. Also, the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin&quot;&gt;&lt;code&gt;lineJoin&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineCap&quot;&gt;&lt;code&gt;lineCap&lt;/code&gt;&lt;/a&gt; properties are set to &lt;code&gt;round&lt;/code&gt; to make edges less sharp and more rounded. Then, the pointer is moved to the coordinates corresponding to the mouse click position employing the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/moveTo&quot;&gt;&lt;code&gt;moveTo&lt;/code&gt;&lt;/a&gt; function.&lt;/p&gt;
&lt;p&gt;On &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event&quot;&gt;&lt;code&gt;mousemove&lt;/code&gt;&lt;/a&gt;, a line to the new coordinates of the mouse position is drawn by harnessing &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineTo&quot;&gt;&lt;code&gt;lineTo&lt;/code&gt;&lt;/a&gt; and then &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/stroke&quot;&gt;&lt;code&gt;stroke&lt;/code&gt;&lt;/a&gt;. Since the mouse position was moved on the &lt;code&gt;mousedown&lt;/code&gt; event, a straight line connecting the new &lt;code&gt;(x, y)&lt;/code&gt; coordinates is drawn.&lt;/p&gt;
&lt;p&gt;Finally, on &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseup_event&quot;&gt;&lt;code&gt;mouseup&lt;/code&gt;&lt;/a&gt;, the drawing operation is stopped by assigning the &lt;code&gt;isDrawing&lt;/code&gt; variable to &lt;code&gt;false&lt;/code&gt;. This flag was introduced to avoid drawing when moving the mouse on canvas without clicking on it first.&lt;/p&gt;
&lt;p&gt;Note that the two just mentioned event-handler functions used to draw are enclosed by &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/beginPath&quot;&gt;&lt;code&gt;beginPath&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/closePath&quot;&gt;&lt;code&gt;closePath&lt;/code&gt;&lt;/a&gt;. This ensures that every line drawn represents a new sub-path for the canvas. This allows each line to be completely independent of the others and to be plotted in the canvas as an unmodifiable drawing element.&lt;/p&gt;
&lt;p&gt;Et voilà! As you can see, implementing a brush to draw on an image in Vanilla JavaScript takes only a few lines of code.&lt;/p&gt;
&lt;h3 id=&quot;2-drawing-in-action&quot;&gt;2. Drawing in Action&lt;/h3&gt;
&lt;p&gt;Now, let’s take a look at the &lt;code&gt;drawOnImage()&lt;/code&gt; function in action through a complete real-world example:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;!&lt;/span&gt;&lt;span&gt;DOCTYPE&lt;/span&gt;&lt;span&gt; html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;#x3C;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;canvas&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        width&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;500&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        height&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;200&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;border: 1px solid black;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;margin-top:5px&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;Size: &amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;range&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        min&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;1&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        max&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;50&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;10&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;size&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;sizeRange&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;margin-top:5px&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;Color: &amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;radio&quot;&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;colorRadio&quot;&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;black&quot;&lt;/span&gt;&lt;span&gt; checked&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;black&quot;&lt;/span&gt;&lt;span&gt;&gt;Black&amp;#x3C;/&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;radio&quot;&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;colorRadio&quot;&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;white&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;black&quot;&lt;/span&gt;&lt;span&gt;&gt;White&amp;#x3C;/&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;radio&quot;&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;colorRadio&quot;&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;red&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;black&quot;&lt;/span&gt;&lt;span&gt;&gt;Red&amp;#x3C;/&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;radio&quot;&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;colorRadio&quot;&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;green&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;black&quot;&lt;/span&gt;&lt;span&gt;&gt;Green&amp;#x3C;/&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;radio&quot;&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;colorRadio&quot;&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;blue&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;black&quot;&lt;/span&gt;&lt;span&gt;&gt;Blue&amp;#x3C;/&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;margin-top:5px&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;clear&quot;&lt;/span&gt;&lt;span&gt;&gt;Clear&amp;#x3C;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;br&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;upload&quot;&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;file&quot;&lt;/span&gt;&lt;span&gt; accept&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;image/*&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      Start drawing on the blank canvas or upload an image and use the brush to&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      modify on it&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;src/index.js&quot;&lt;/span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; fileInput&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;#upload&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// enabling drawing on the blank canvas&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;drawOnImage&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fileInput.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;change&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; fileInput.files;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // displaying the uploaded image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; image&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;createElement&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;img&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  image.src &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; fileToDataUri&lt;/span&gt;&lt;span&gt;(file);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // enabling the brush after the image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // has been uploaded&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  image.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;load&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    drawOnImage&lt;/span&gt;&lt;span&gt;(image);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; fileToDataUri&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Promise&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; reader&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; FileReader&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    reader.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;load&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      resolve&lt;/span&gt;&lt;span&gt;(reader.result);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    reader.&lt;/span&gt;&lt;span&gt;readAsDataURL&lt;/span&gt;&lt;span&gt;(field);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; sizeElement&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;#sizeRange&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; size &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; sizeElement.value;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;sizeElement.&lt;/span&gt;&lt;span&gt;oninput&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  size &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; e.target.value;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; colorElement&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;getElementsByName&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;colorRadio&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; color;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;colorElement.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (c.checked) color &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; c.value;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;colorElement.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  c.&lt;/span&gt;&lt;span&gt;onclick&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    color &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; c.value;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; drawOnImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; canvasElement&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;canvas&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; context&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; canvasElement.&lt;/span&gt;&lt;span&gt;getContext&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;2d&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // if an image is present,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // the image passed as parameter is drawn in the canvas&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (image) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; imageWidth&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; image.width;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; imageHeight&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; image.height;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // rescaling the canvas element&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    canvasElement.width &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; imageWidth;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    canvasElement.height &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; imageHeight;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    context.&lt;/span&gt;&lt;span&gt;drawImage&lt;/span&gt;&lt;span&gt;(image, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, imageWidth, imageHeight);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; clearElement&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;clear&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  clearElement.&lt;/span&gt;&lt;span&gt;onclick&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    context.&lt;/span&gt;&lt;span&gt;clearRect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, canvasElement.width, canvasElement.height);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; isDrawing;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  canvasElement.&lt;/span&gt;&lt;span&gt;onmousedown&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    isDrawing &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    context.&lt;/span&gt;&lt;span&gt;beginPath&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    context.lineWidth &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; size;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    context.strokeStyle &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; color;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    context.lineJoin &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;round&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    context.lineCap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;round&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    context.&lt;/span&gt;&lt;span&gt;moveTo&lt;/span&gt;&lt;span&gt;(e.clientX, e.clientY);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  canvasElement.&lt;/span&gt;&lt;span&gt;onmousemove&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (isDrawing) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      context.&lt;/span&gt;&lt;span&gt;lineTo&lt;/span&gt;&lt;span&gt;(e.clientX, e.clientY);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      context.&lt;/span&gt;&lt;span&gt;stroke&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  canvasElement.&lt;/span&gt;&lt;span&gt;onmouseup&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; function&lt;/span&gt;&lt;span&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    isDrawing &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    context.&lt;/span&gt;&lt;span&gt;closePath&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the HTML page contains everything needed to change brush size, and color. By interacting with them, users can achieve several possibilities and unleash their creativity. Also, in case of errors, they can press the &lt;em&gt;Clear&lt;/em&gt; button and return to the initial conditions.&lt;/p&gt;
&lt;p&gt;From a technical point of view is that the &lt;code&gt;input&lt;/code&gt; element is used to upload an optional image. Then, it is passed to the aforementioned &lt;code&gt;drawOnImage()&lt;/code&gt; function. This enables the brush feature on the image or the blank canvas and lets users draw on it. Please, note that the example that was just implemented corresponds to the fiddle you can find at the beginning of the article.&lt;/p&gt;
&lt;h2 id=&quot;final-considerations&quot;&gt;Final Considerations&lt;/h2&gt;
&lt;p&gt;Although implementing a brush feature to draw on an image in Vanilla JavaScript is definitely an easy task, there are a few downsides that must be addressed. First, making the drawing process smooth while moving the pointer quickly is tricky. This is an optimization problem, which may require you to adopt caching or debouncing techniques. Thus, implementing this feature effectively and efficiently can easily turn into a nightmare. Second, whenever you want to add a new style to the brush, you must spend time implementing all the front-end components required to enhance this feature. Despite not being difficult, this definitely involves boilerplate code.&lt;/p&gt;
&lt;p&gt;When you do not want to spend hours writing boring yet unavoidable code to add new colors or brushes, a commercial and all-in-one solution like &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;&lt;code&gt;PhotoEditorSDK&lt;/code&gt;&lt;/a&gt; should be the preferred approach. In fact, it allows you to no longer stressing due to having to tackle these tedious and time-consuming tasks. Plus, whenever you need help, you can ask for support from the &lt;a href=&quot;https://img.ly/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;IMG.LY&lt;/a&gt; developers who built the SDK.&lt;/p&gt;
&lt;h2 id=&quot;drawing-on-an-image-with-photoeditorsdk&quot;&gt;Drawing on an Image with &lt;code&gt;photoeditorsdk&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;First, you should read &lt;a href=&quot;https://img.ly/docs/pesdk/web/guides/umd/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;integration tutorial&lt;/a&gt; article from &lt;a href=&quot;https://img.ly/docs/pesdk/guides/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;the official documentation&lt;/a&gt; on how to get started with &lt;code&gt;PhotoEditorSDK&lt;/code&gt; in HTML and JavaScript. Then, by using the &lt;a href=&quot;https://img.ly/docs/pesdk/web/features/brush/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;&lt;em&gt;Brush Engine&lt;/em&gt;&lt;/a&gt; you can start enjoying a brush feature optimized for touch screen interaction. Moreover, it natively supports different brush strokes. They can also be tweaked in terms of color, hardness, size, and opacity. This way, you should be able to achieve the desired result:&lt;br&gt;
&lt;img src=&quot;https://user-images.githubusercontent.com/72130027/130465683-41d7bcc6-0261-4959-befb-f70633bbd0a3.gif&quot; alt=&quot;draw_with_PESDK&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this article, we looked at how to draw on an image with JavaScript. Implementing a brush to draw on images by using the &lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt; HTML element is not complex. However, it inevitably involves boilerplate code. Specifically, when implementing all the minor options such a feature should offer to the end-users. Also, making the brush smooth and optimized for touch screen and fast movements is quite complex. In fact, this requires optimizing or devising an intelligent algorithm aimed at not wasting resources. So, if you want to avoid these issues and use an easy an all-in-one solution, you should consider using more advanced and complete software – such as &lt;a href=&quot;https://img.ly/products/creative-sdk/&quot;&gt;CreativeEditor SDK&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for reading! We hope that you found this article helpful. Feel free to reach out to us on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt; with any questions, comments, or suggestions.&lt;br&gt;
Like to try out our product? &lt;a href=&quot;https://img.ly/forms/contact-sales/&quot;&gt;Get in touch&lt;/a&gt; or start a &lt;a href=&quot;https://img.ly/docs/cesdk/&quot;&gt;free trail&lt;/a&gt; today!&lt;/p&gt;</content:encoded><dc:creator>Antonello</dc:creator><media:content url="https://blog.img.ly/2021/08/draw-with-javascript-psdk.png" medium="image"/><category>JavaScript</category><category>App Development</category><category>HTML5</category><category>Web Development</category><category>Photo Editing</category><category>Developer Tools</category><category>Code</category><category>Tutorial</category><category>Tech</category><category>Software Development</category><category>How-To</category></item><item><title>How To Resize an Image in React</title><link>https://img.ly/blog/how-to-resize-an-image-in-react/</link><guid isPermaLink="true">https://img.ly/blog/how-to-resize-an-image-in-react/</guid><description>Quickly resize an image with the `react-image-file-resizer` React library.</description><pubDate>Fri, 30 Jul 2021 10:57:19 GMT</pubDate><content:encoded>&lt;p&gt;In this article, you will see how to resize an image in JavaScript. In particular, here you will learn to achieve this goal with the &lt;code&gt;react-image-file-resizer&lt;/code&gt; React library. If you are looking for a pure Javascript solution, &lt;a href=&quot;https://img.ly/blog/how-to-resize-an-image-with-javascript/&quot;&gt;here’s a quick rundown of the HTML API usage&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Providing users with features to resize images has become almost unavoidable. This is because images are larger than ever. It is not a secret that the quality of the images and therefore their file sizes have been increasing for years.&lt;/p&gt;
&lt;p&gt;The problem is that dealing with large files is time-consuming. Plus, it may cost money in bandwidth when uploading or downloading them. This is why it is so important to shrink the size of images by resizing them. Also, these issues fall on end-users, and this should be avoided.&lt;/p&gt;
&lt;p&gt;So, let’s see how to resize an image in React with &lt;code&gt;react-image-file-resizer&lt;/code&gt;. By following this step-by-step tutorial, you will achieve the following &lt;a href=&quot;https://codesandbox.io/s/how-to-resize-an-image-in-react-demo-forked-ip6fi&quot;&gt;result&lt;/a&gt;:&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;This is the list of all the prerequisites for the demo application you are going to build:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.npmjs.com/getting-started/&quot;&gt;Node.js and npm 5.2+ and higher&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/react-resize-image&quot;&gt;&lt;code&gt;react-image-file-resizer&lt;/code&gt;&lt;/a&gt; &gt;= 0.4.7&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;resizing-an-image-with-react-image-file-resizer&quot;&gt;Resizing an Image with &lt;code&gt;react-image-file-resizer&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;You can clone the &lt;a href=&quot;https://github.com/imgly/Blog-How-To-Resize-an-Image-in-React&quot;&gt;GitHub repository that supports this article&lt;/a&gt; and try the demo application by launching the following commands:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git clone https://github.com/Tonel/resize-image-react-demo-imgly&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cd resize-image-react-demo-imgly&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm i&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm start&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Otherwise, you can continue following this tutorial and build the demo application step by step.&lt;/p&gt;
&lt;h2 id=&quot;1-creating-a-react-project&quot;&gt;1. Creating a React Project&lt;/h2&gt;
&lt;p&gt;Generate an empty working project in React with &lt;a href=&quot;https://create-react-app.dev/docs/getting-started/&quot;&gt;Create React App&lt;/a&gt;, the officially supported way to create single-page React applications. You can initialize a new project called &lt;code&gt;react-image-resizer-demo&lt;/code&gt; with the following command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npx create-react-app react-image-resizer-demo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will now have a demo project located in the &lt;code&gt;react-image-resizer-demo&lt;/code&gt; folder with the following file structure:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;react-image-resizer-demo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;├── README.md&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;├── node_modules&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;├── package.json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;├── .gitignore&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;├── public&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;│   ├── favicon.ico&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;│   ├── index.html&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;│   ├── logo192.png&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;│   ├── logo512.png&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;│   ├── manifest.json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;│   └── robots.txt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;└── src&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├── App.css&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├── App.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├── App.test.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├── index.css&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├── index.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├── logo.svg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├── reportWebVitals.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    └── setupTests.js&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move into the &lt;code&gt;react-image-resizer-demo&lt;/code&gt; folder and start a local server by launching these commands:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cd react-image-resizer-demo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm start&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reach &lt;a href=&quot;http://localhost:3000/&quot;&gt;http://localhost:3000/&lt;/a&gt; in your browser. You should now be able to see the default Create React App screen, as follows:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;The default Create React App screen&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 983px) 983px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;983&quot; height=&quot;728&quot; src=&quot;https://img.ly/_astro/s_D2E103F8349A30F6AA8E27CD2BA4B6EDB946A8DEF5B8272009321B4F51D679F9_1624366125534_image_ZvCahg.webp&quot; srcset=&quot;/_astro/s_D2E103F8349A30F6AA8E27CD2BA4B6EDB946A8DEF5B8272009321B4F51D679F9_1624366125534_image_1hpMyh.webp 640w, /_astro/s_D2E103F8349A30F6AA8E27CD2BA4B6EDB946A8DEF5B8272009321B4F51D679F9_1624366125534_image_Zy6hVM.webp 750w, /_astro/s_D2E103F8349A30F6AA8E27CD2BA4B6EDB946A8DEF5B8272009321B4F51D679F9_1624366125534_image_Z184VNu.webp 828w, /_astro/s_D2E103F8349A30F6AA8E27CD2BA4B6EDB946A8DEF5B8272009321B4F51D679F9_1624366125534_image_ZvCahg.webp 983w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;2-installing-react-image-file-resizer&quot;&gt;2. Installing &lt;code&gt;react-image-file-resizer&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;First, you have to add the &lt;code&gt;react-image-file-resizer&lt;/code&gt; library to your project’s dependencies. You can do it by running the following command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm install --save react-image-file-resizer&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Thus, your &lt;code&gt;package.json&lt;/code&gt; file will be updated accordingly. You should now be able to spot &lt;code&gt;react-image-file-resizer&lt;/code&gt; as a dependency.&lt;/p&gt;
&lt;p&gt;Now, all prerequisites have been met. So, you can start building your image resizer component. Let’s see together how.&lt;/p&gt;
&lt;h2 id=&quot;3-building-the-image-resizer-component&quot;&gt;3. Building the Image Resizer Component&lt;/h2&gt;
&lt;p&gt;First, make a &lt;code&gt;components&lt;/code&gt; folder inside &lt;code&gt;src&lt;/code&gt;. Then, create an &lt;code&gt;ImageResizer&lt;/code&gt; folder containing &lt;code&gt;index.js&lt;/code&gt; and &lt;code&gt;index.css&lt;/code&gt;. These two files will contain the resizer component definition and style respectively.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://paper-attachments.dropbox.com/s_0C9FF6F84F9D5BBDC6112B53688A82A0D901A542FB0DD0CC04FE4E4B7D0590ED_1626875270825_image.png&quot; alt=&quot;The components folder&quot;&gt;&lt;/p&gt;
&lt;p&gt;Initialize &lt;code&gt;index.js&lt;/code&gt; with the following snippet:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; React &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; ImageResizer&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; &amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;{&lt;/span&gt;&lt;span&gt;/*TODO*/&lt;/span&gt;&lt;span&gt;}&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; default&lt;/span&gt;&lt;span&gt; ImageResizer;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way, you have just defined an empty &lt;code&gt;ImageResizer&lt;/code&gt; component.&lt;br&gt;
Now, import the &lt;code&gt;Resizer&lt;/code&gt; utility from the &lt;code&gt;react-image-file-resizer&lt;/code&gt; library. Add it to the &lt;code&gt;ImageResizer&lt;/code&gt; imports:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import Resizer from &apos;react-image-file-resizer&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is what the final &lt;code&gt;ImageResizer&lt;/code&gt; will look like:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import React, {useEffect, useState} from &quot;react&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import Resizer from &quot;react-image-file-resizer&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function ImageResize(props) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const {imageToResize, onImageResized, resizeAspect, resizeQuality} = props;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const [imageToResizeUri, setImageToResizeUri] = useState();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const [imageToResizeWidth, setImageToResizeWidth] = useState();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const [imageToResizeHeight, setImageToResizeHeight] = useState();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    useEffect(() =&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        if (imageToResize) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            const reader = new FileReader();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            reader.addEventListener(&apos;load&apos;, () =&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                setImageToResizeUri(reader.result);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            reader.readAsDataURL(imageToResize);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }, [imageToResize])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    useEffect(() =&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        if (imageToResize &amp;#x26;&amp;#x26; imageToResizeWidth &amp;#x26;&amp;#x26; imageToResizeHeight) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            Resizer.imageFileResizer(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                imageToResize,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                imageToResizeWidth * resizeAspect,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                imageToResizeWidth * resizeAspect,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                &quot;JPEG&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                resizeQuality,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                0,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                (uri) =&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    onImageResized(uri)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                &quot;base64&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }, [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        imageToResize, imageToResizeWidth, imageToResizeHeight,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        onImageResized, resizeAspect, resizeQuality&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;img&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            src={imageToResizeUri}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            onLoad= {(e) =&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                const img = e.target;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                setImageToResizeWidth(img.width);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                setImageToResizeHeight(img.height);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            crossorigin=&quot;anonymous&quot; // to avoid CORS-related problems&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ImageResize.defaultProps = {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    onImageResized: () =&gt; {},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    resizeAspect: 0.5,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    resizeQuality: 100&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;export default ImageResize;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;imageToResize&lt;/code&gt; is the source &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Blob&quot;&gt;Blob&lt;/a&gt; representing the image received from the props. First, &lt;code&gt;imageToResize&lt;/code&gt; is transformed into a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Base64&quot;&gt;&lt;code&gt;Base64&lt;/code&gt;&lt;/a&gt; URI by the first &lt;a href=&quot;https://legacy.reactjs.org/docs/hooks-effect.html&quot;&gt;&lt;code&gt;useEffect()&lt;/code&gt;&lt;/a&gt; function, and then showed. After being loaded by the &lt;code&gt;&amp;#x3C;img&gt;&lt;/code&gt; HTML tag, the image’s width and height are saved to be used during the resizing operation. This is done by the second &lt;code&gt;useEffect()&lt;/code&gt; function, which takes care of resizing the Blob image received by employing the &lt;code&gt;Resizer&lt;/code&gt; utility.&lt;/p&gt;
&lt;p&gt;Please, note that &lt;code&gt;resizeAspect&lt;/code&gt; and &lt;code&gt;resizeQuality&lt;/code&gt; are two props in charge of defining the resize aspect and quality percentage, respectively. By default, they are assigned to 0.5 and 100. This means that the resized image will be 50% smaller and no compression will be applied during the resizing process.&lt;/p&gt;
&lt;h2 id=&quot;4-putting-it-all-together&quot;&gt;4. Putting It All Together&lt;/h2&gt;
&lt;p&gt;Now, let’s see the &lt;code&gt;ImageResizer&lt;/code&gt; component defined above in action. All you need to do, is change the &lt;code&gt;App.js&lt;/code&gt; file as follows:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; React, { useState } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &apos;./App.css&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; ImageResize &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./components/ImageResizer&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; App&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;imageToResize&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setImageToResize&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;resizedImage&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setResizedImage&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; onUploadFile&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (event.target.files &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; event.target.files.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt; &gt;&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      setImageToResize&lt;/span&gt;&lt;span&gt;(event.target.files[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;app&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;Image Resizer&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        Please, upload an image and it will be showed both original and resized&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        by 50%&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;file&quot;&lt;/span&gt;&lt;span&gt; accept&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;image/*&quot;&lt;/span&gt;&lt;span&gt; onChange&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{onUploadFile} /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;&lt;/span&gt;&lt;span&gt;ImageResize&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          imageToResize&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{imageToResize}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          onImageResized&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{(&lt;/span&gt;&lt;span&gt;resizedImage&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; setResizedImage&lt;/span&gt;&lt;span&gt;(resizedImage)}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      {resizedImage &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          &amp;#x3C;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;Resized Image&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          &amp;#x3C;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Resize Image&quot;&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{resizedImage} /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      )}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; default&lt;/span&gt;&lt;span&gt; App;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;input&lt;/code&gt; element allows users to upload an image. This is stored in the &lt;code&gt;imageToResize&lt;/code&gt; state variable, and then passed to &lt;code&gt;ImageResizer&lt;/code&gt; as a props. This will take care of resizing it accordingly. The resulting resized image is saved thanks to the &lt;code&gt;setResizedImage&lt;/code&gt; function, and finally displayed in the &lt;em&gt;Resize Image&lt;/em&gt; section.&lt;/p&gt;
&lt;p&gt;If you are a Next.js user, you can use the &lt;a href=&quot;https://writech.run/blog/how-to-make-next-js-image-optimization-work-on-aws-elastic-beanstalk-2776ea255eff/&quot;&gt;&lt;code&gt;&amp;#x3C;Image /&gt;&lt;/code&gt; component to make it resize images for you&lt;/a&gt;. React will then render resized images automatically.&lt;/p&gt;
&lt;h2 id=&quot;final-considerations-on-react-image-file-resizer&quot;&gt;Final Considerations on &lt;code&gt;react-image-file-resizer&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Resizing an image cannot be considered a complex task. However, using a library like &lt;code&gt;react-image-file-resizer&lt;/code&gt; makes everything easier. In fact, you can achieve your goal with just a handful of lines of code. On the other hand, you should take into account what using such specific libraries implies. In fact, when you need to perform many image-related operations, you may be ending up with as many libraries as operations required.&lt;/p&gt;
&lt;p&gt;Not only might they be complex to make them coexist, but they may have very different UIs as well. This is why, harnessing a commercial and more complete solution such as &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;&lt;code&gt;PhotoEditorSDK&lt;/code&gt;&lt;/a&gt; could be a better approach. With only one library, you would get several tools to deal with images as you need. This, while preserving the consistency of your application’s UI. Plus, whenever you need help, you can ask for support from the &lt;a href=&quot;https://img.ly/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;img.ly&lt;/a&gt; developers who built the SDK.&lt;/p&gt;
&lt;h2 id=&quot;resizing-an-image-with-photoeditorsdk&quot;&gt;Resizing an Image with PhotoEditorSDK&lt;/h2&gt;
&lt;p&gt;First, you should read &lt;a href=&quot;https://img.ly/docs/pesdk/web/introduction/getting_started/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;this&lt;/a&gt; article from &lt;a href=&quot;https://img.ly/docs/pesdk/guides/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;the official documentation&lt;/a&gt; on how to get started with &lt;code&gt;PhotoEditorSDK&lt;/code&gt; in React. Then, by using the &lt;a href=&quot;https://img.ly/docs/pesdk/web/features/transform/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;transform tool&lt;/a&gt; you can perform cropping, &lt;a href=&quot;https://img.ly/docs/pesdk/web/features/transform/#image-resizing?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;resizing&lt;/a&gt;, flipping, and rotation operations with just one feature. This way, you should be able to achieve the desired result&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;resize-image-with-pesdk-react&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1095px) 1095px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1095&quot; height=&quot;652&quot; src=&quot;https://img.ly/_astro/resize-image-with-pesdk-react_Z2rpOL5.webp&quot; srcset=&quot;/_astro/resize-image-with-pesdk-react_Z25Tfyt.webp 640w, /_astro/resize-image-with-pesdk-react_21L4Vs.webp 750w, /_astro/resize-image-with-pesdk-react_Zk4j8T.webp 828w, /_astro/resize-image-with-pesdk-react_WzXl1.webp 1080w, /_astro/resize-image-with-pesdk-react_Z2rpOL5.webp 1095w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this article, we looked at how to resize an image in React. Although this cannot be considered a complex feature to implement, using a library such as &lt;code&gt;react-image-file-resizer&lt;/code&gt; is recommended. As we have seen, you can resize an image effortlessly and with only a few lines of code. On the other hand, it is a library with a very specific and limited purpose. So, if you needed to perform more than one operation on your images, you might want to take advantage of a more advanced and complete solution – such as &lt;code&gt;PhotoEditorSDK&lt;/code&gt;.&lt;/p&gt;</content:encoded><dc:creator>Antonello</dc:creator><media:content url="https://blog.img.ly/2021/07/resize-image-with-react-1.png" medium="image"/><category>React</category><category>App Development</category><category>Mobile App Development</category><category>Photo Editing</category><category>Tutorial</category><category>Tech</category><category>Developer</category><category>Code</category><category>Developer Tools</category><category>React Native</category><category>Software Development</category><category>How-To</category></item><item><title>How To Resize an Image With JavaScript</title><link>https://img.ly/blog/how-to-resize-an-image-with-javascript/</link><guid isPermaLink="true">https://img.ly/blog/how-to-resize-an-image-with-javascript/</guid><description>Today, you will learn how to resize an image in JavaScript, without using any external library. Thanks to the HTML &lt;canvas&gt; element, this is reasonably easy. Let&apos;s go!</description><pubDate>Tue, 20 Jul 2021 09:22:31 GMT</pubDate><content:encoded>&lt;p&gt;In this article, you will learn how to resize an image in JavaScript, without using any external library. Thanks to the HTML &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API&quot;&gt;&lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt;&lt;/a&gt; element, this is a reasonably easy task to accomplish.&lt;/p&gt;
&lt;p&gt;Resizing an image has become increasingly important. That is because the quality of the images and their file sizes have been increasing for years.&lt;br&gt;
For example, when letting users upload an image, you should always consider resizing it before uploading it. Uploading large photos is time-consuming and may cost money in bandwidth.  As we have previously explained you might also want to &lt;a href=&quot;https://img.ly/blog/how-to-compress-an-image-before-uploading-it-in-javascript/&quot;&gt;compress such images&lt;/a&gt;.  As you can imagine, both of these consequences fall on end-users – we want to avoid this.&lt;/p&gt;
&lt;p&gt;Let’s see how to resize an image with Vanilla JavaScript. Follow this step-by-step tutorial to achieve the following result on &lt;a href=&quot;https://codesandbox.io/s/blog-how-to-resize-an-image-with-javascript-forked-mkuri&quot;&gt;CodeSandbox&lt;/a&gt;:&lt;/p&gt;
&lt;iframe src=&quot;https://codesandbox.io/embed/blog-how-to-resize-an-image-with-javascript-forked-mkuri&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;resizing-images-using-canvas&quot;&gt;Resizing Images Using &lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;You can clone the &lt;a href=&quot;https://github.com/Tonel/resize-image-javascript-imgly&quot;&gt;GitHub repository that supports this article&lt;/a&gt; by launching the following command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git clone https://github.com/Tonel/resize-image-javascript-imgly&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Otherwise, you can continue following this tutorial and build the demo application step by step.&lt;/p&gt;
&lt;h3 id=&quot;1-implementing-the-resizing-logic&quot;&gt;1. Implementing the Resizing Logic&lt;/h3&gt;
&lt;p&gt;While in a &lt;a href=&quot;https://img.ly/blog/how-to-resize-an-image-in-react/&quot;&gt;React context we used a library&lt;/a&gt; to do the heavy lifting for us, knowing the HTML &lt;code&gt;[&amp;#x3C;canvas&gt;](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API)&lt;/code&gt; API is the only prerequisites for this approach. Although typically used for animation, game graphics, data visualization, and real-time video processing, you can also employ it as an image manipulation tool. Specifically, it provides everything required to resize an image. Let’s see how:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; resizeImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;imgToResize&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;resizingFactor&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 0.5&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; canvas&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;createElement&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;canvas&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; context&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; canvas.&lt;/span&gt;&lt;span&gt;getContext&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;2d&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; originalWidth&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; imgToResize.width;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; originalHeight&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; imgToResize.height;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; canvasWidth&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; originalWidth &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; resizingFactor;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; canvasHeight&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; originalHeight &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; resizingFactor;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  canvas.width &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; canvasWidth;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  canvas.height &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; canvasHeight;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  context.&lt;/span&gt;&lt;span&gt;drawImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    imgToResize,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    originalWidth &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; resizingFactor,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    originalHeight &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; resizingFactor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; canvas.&lt;/span&gt;&lt;span&gt;toDataURL&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Initialize an in-memory &lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt; with a 2D rendering context for the drawing surface.&lt;/li&gt;
&lt;li&gt;Set height and width depending on the size of the original image to resize and the resizing factor.&lt;/li&gt;
&lt;li&gt;Then, the resized image is drawn according to the resizing factor passed by harnessing the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage&quot;&gt;&lt;code&gt;drawImage()&lt;/code&gt;&lt;/a&gt; function.&lt;/li&gt;
&lt;li&gt;Finally, the resized image is returned.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Et voilà! As you can see, resizing an image in Vanilla JavaScript takes only a few lines of code.&lt;/p&gt;
&lt;h3 id=&quot;2-resizing-in-action&quot;&gt;2. Resizing in Action&lt;/h3&gt;
&lt;p&gt;Now, it is time to see the &lt;code&gt;resizeImage()&lt;/code&gt; function in action through a simple example.&lt;br&gt;
&lt;em&gt;index.html&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;!&lt;/span&gt;&lt;span&gt;DOCTYPE&lt;/span&gt;&lt;span&gt; html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;#x3C;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;Image Resizer&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      Please, upload an image and it will be shown both original and resized by&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      50%&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;upload&quot;&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;file&quot;&lt;/span&gt;&lt;span&gt; accept&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;image/*&quot;&lt;/span&gt;&lt;span&gt; required&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;images&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;Original image&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;imgToResize&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;Resized image&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;resizedImage&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;src/index.js&quot;&lt;/span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;src/index.js&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// hiding the div that will contain the images&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; imagesDiv&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;#images&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;imagesDiv.style.visibility &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;hidden&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; fileInput&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;#upload&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fileInput.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;change&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; fileInput.files;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // displaying the uploaded image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; imageToResize&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;#imgToResize&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  imageToResize.src &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; fileToDataUri&lt;/span&gt;&lt;span&gt;(file);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // resizing the image and displaying it&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; resizedImage&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;#resizedImage&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  resizedImage.src &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; resizeImage&lt;/span&gt;&lt;span&gt;(imageToResize, &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // making the div containing the image visible&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  imagesDiv.style.visibility &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;visible&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; fileToDataUri&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Promise&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; reader&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; FileReader&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    reader.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;load&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      resolve&lt;/span&gt;&lt;span&gt;(reader.result);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    reader.&lt;/span&gt;&lt;span&gt;readAsDataURL&lt;/span&gt;&lt;span&gt;(field);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; resizeImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;imgToResize&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;resizingFactor&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 0.5&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; canvas&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;createElement&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;canvas&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; context&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; canvas.&lt;/span&gt;&lt;span&gt;getContext&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;2d&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; originalWidth&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; imgToResize.width;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; originalHeight&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; imgToResize.height;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; canvasWidth&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; originalWidth &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; resizingFactor;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; canvasHeight&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; originalHeight &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; resizingFactor;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  canvas.width &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; canvasWidth;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  canvas.height &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; canvasHeight;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  context.&lt;/span&gt;&lt;span&gt;drawImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    imgToResize,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    originalWidth &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; resizingFactor,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    originalHeight &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; resizingFactor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; canvas.&lt;/span&gt;&lt;span&gt;toDataURL&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;&amp;#x3C;input&gt;&lt;/code&gt; element allows users to upload an image, then passed to the &lt;code&gt;resizeImage()&lt;/code&gt; function. This is in charge of generating the resized image, which is finally shown below the original image. Please, note that this example is the same presented in the fiddle placed at the beginning of the article.&lt;/p&gt;
&lt;h2 id=&quot;final-considerations&quot;&gt;Final Considerations&lt;/h2&gt;
&lt;p&gt;Resizing an image in Vanilla JavaScript is definitely an easy task. No external libraries are required, and you can achieve your goal with just a few lines of code. This is great! On the other hand, &lt;a href=&quot;https://entropymine.com/resamplescope/notes/browsers/&quot;&gt;every browser&lt;/a&gt; applies a different image interpolation algorithm behind the scenes. This leads to results depending on the user’s preferred browser. Not only might this be a problem in terms of consistency, but also a source of quality issues. Plus, such a simple task can turn into a nightmare if you wanted to implement a proper UI, allowing users to resize an image graphically.&lt;/p&gt;
&lt;p&gt;In that case, a commercial and comprehensive solution like &lt;code&gt;[PhotoEditorSDK](https://img.ly/products/photo-sdk/)&lt;/code&gt; is a more beneficial approach. In fact, you should no longer have to worry about inconsistencies or quality issues. Moreover, with only one library, you get several tools to treat images without having to implement the application’s UI. Also, whenever you need help, you can ask for support from the &lt;a href=&quot;https://img.ly/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;IMG.LY&lt;/a&gt; developers who built the SDK.&lt;/p&gt;
&lt;h2 id=&quot;resizing-an-image-with-photoeditor-sdk&quot;&gt;Resizing an Image with PhotoEditor SDK&lt;/h2&gt;
&lt;p&gt;First, you should read &lt;a href=&quot;https://img.ly/docs/pesdk/web/guides/umd/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;this&lt;/a&gt; article from &lt;a href=&quot;https://img.ly/docs/pesdk/guides/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;the official documentation&lt;/a&gt; on how to get started with &lt;code&gt;PhotoEditorSDK&lt;/code&gt; in HTML and JavaScript. Then, by using the &lt;a href=&quot;https://img.ly/docs/pesdk/web/features/transform/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;transform tool&lt;/a&gt; you can perform cropping, &lt;a href=&quot;https://img.ly/docs/pesdk/web/features/transform/#image-resizing?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;resizing&lt;/a&gt;, flipping, and rotation operations with just one feature. This way, you should be able to achieve the desired result:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Transform Images with PhotoEditor SDK&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 800px) 800px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;800&quot; height=&quot;476&quot; src=&quot;https://img.ly/_astro/resize-image-with-javascript_1gJOFK.webp&quot; srcset=&quot;/_astro/resize-image-with-javascript_Z20b4Fi.webp 640w, /_astro/resize-image-with-javascript_Z1Ep5cX.webp 750w, /_astro/resize-image-with-javascript_1gJOFK.webp 800w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this article, we looked at how to resize an image with JavaScript. Resizing an image by using &lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt; is not a complex task, although this solution might introduce quality issues. As we have seen, you can resize an image effortlessly and with only a handful of lines of code. On the other hand, every browser applies a different resizing algorithm, leading to inconsistent results. For consistent and reliable results, you should take advantage of a more advanced and complete solution – such as &lt;code&gt;PhotoEditorSDK&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for reading! We hope that you found this article helpful. Feel free to reach out to us on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt; with any questions, comments, or suggestions.&lt;/p&gt;</content:encoded><dc:creator>Antonello</dc:creator><media:content url="https://blog.img.ly/2021/07/resize-image-with-javascript-pesdk.png" medium="image"/><category>JavaScript</category><category>Photo Editing</category><category>Photo Editor</category><category>Web Development</category><category>Software Development</category><category>App Development</category><category>Developer Tools</category><category>Code</category><category>Tech</category><category>How-To</category><category>Tutorial</category></item><item><title>A Photo and Video Editor for React Native Apps</title><link>https://img.ly/blog/a-photo-and-video-editor-for-your-react-native-apps/</link><guid isPermaLink="true">https://img.ly/blog/a-photo-and-video-editor-for-your-react-native-apps/</guid><pubDate>Fri, 29 May 2020 10:30:01 GMT</pubDate><content:encoded>&lt;hr&gt;
&lt;h3 id=&quot;how-to-integrate-a-photo-and-video-editor-into-your-react-native-app&quot;&gt;How to integrate a Photo and Video Editor into your React Native App&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;This tutorial walks you through the integration process of PhotoEditor SDK and VideoEditor SDK into your React Native app for iOS and Android. You’ll learn how to use our React Native modules to facilitate the integration and to customize our editors. For this tutorial we presume that all the necessary development tools for building an iOS and Android app are met, so make sure to complete the official &lt;a href=&quot;https://reactnative.dev/docs/getting-started&quot;&gt;React Native CLI Quickstart&lt;/a&gt; guides for iOS and Android beforehand.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Please make sure to acquire the licenses for &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; and &lt;a href=&quot;https://img.ly/products/video-sdk/&quot;&gt;VideoEditor SDK&lt;/a&gt; before integrating them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/embed/e9JiCMQKrJY?feature=oembed&quot;&gt;https://www.youtube.com/embed/e9JiCMQKrJY?feature=oembed&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this tutorial, we’re going to show how to integrate PhotoEditor SDK and VideoEditor SDK for iOS and Android into your React Native app. Therefore, we created React Native modules for our products to simplify this process for you as much as possible. We’re going to use &lt;a href=&quot;https://github.com/imgly/vesdk-react-native&quot;&gt;VideoEditor SDK’s README&lt;/a&gt;, which is in most parts identical to the PhotoEditor SDK’s README, and Visual Studio Code. So, let’s get started.&lt;/p&gt;
&lt;p&gt;First, we create a React Native project with the name “Demo” based on the default template by using the command &lt;code&gt;npx react-native init Demo&lt;/code&gt;. The project will now be initialized and automatically install all dependencies of the current React Native version. Afterwards, we can find the new React Native project ready to use in the folder “Demo”. So, we’ll speed this up a little.&lt;/p&gt;
&lt;p&gt;We already prepared another folder with resources and assets that we want to integrate in our app. Here we chose an image, the required licenses for our PhotoEditor SDK and VideoEditor SDK for both target platforms, a video and two logos that we will later use to show how we can customize our editors. We copy these resources into the root of our project to make the resources accessible for our app.&lt;/p&gt;
&lt;p&gt;Now, we switch to the folder of the “Demo” project*.* We can now copy and execute the command &lt;code&gt;yarn add react-native-videoeditorsdk&lt;/code&gt; from the README to install the dependencies to the React Native module for our VideoEditor SDK … and to the React Native module for PhotoEditor SDK by issuing the command &lt;code&gt;yarn add react-native-photoeditorsdk&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, we’re going to set up the dependencies for our native iOS libraries. We can simply copy the command &lt;code&gt;cd ios &amp;#x26;&amp;#x26; pod install &amp;#x26;&amp;#x26; cd ..&lt;/code&gt; from the README and execute it to install all iOS dependencies. They include the native PhotoEditor SDK and VideoEditor SDK libraries that are required by our React Native modules.&lt;/p&gt;
&lt;p&gt;And now, we set up the dependencies for our native Android libraries. The required steps that we will now take are described in detail in the README.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;android {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  defaultConfig {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    multiDexEnabled true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dependencies {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  implementation ‘androidx.multidex:multidex:2.0.1’&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We copy the lines and add them at the end of our &lt;code&gt;android/app/build.gradle&lt;/code&gt; file. Now we need to change the superclass of our &lt;code&gt;MainApplication&lt;/code&gt; class to enable Multidex. Next, we add the img.ly repository and the plugin by copying the following lines and add them at the top of our &lt;code&gt;android/build.gradle&lt;/code&gt; file located in our Android folder.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;buildscript {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  repositories {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    jcenter()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    maven { url &quot;https://plugins.gradle.org/m2/&quot; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    maven { url &quot;https://artifactory.img.ly/artifactory/imgly&quot; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  dependencies {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    classpath &quot;org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    classpath &apos;ly.img.android.sdk:plugin:7.1.8&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can configure our PhotoEditor SDK and VideoEditor SDK by opening the &lt;code&gt;android/app/build.gradle&lt;/code&gt; file and add these lines under &lt;code&gt;apply plugin: “com.android.application&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;apply plugin: &apos;ly.img.android.sdk&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;apply plugin: &apos;kotlin-android&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Comment out the modules you don&apos;t need, to save size.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;imglyConfig {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  modules {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:text&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:focus&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:frame&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:brush&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:filter&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:sticker&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:overlay&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:transform&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:adjustment&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:text-design&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;ui:video-trim&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // This module is big, remove the serializer if you don&apos;t need that feature.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;backend:serializer&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // Remove the asset packs you don&apos;t need, these are also big in size.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;assets:font-basic&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;assets:frame-basic&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;assets:filter-basic&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;assets:overlay-basic&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;assets:sticker-shapes&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    include &apos;assets:sticker-emoticons&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Getting back to our iOS version, we can now launch our demo project on iOS, which will currently look like a plain React Native project that we initialized with the first command.&lt;/p&gt;
&lt;p&gt;The main difference to an off-the-shelf React Native project is that our React Native modules are installed and ready-to-use in the &lt;code&gt;App.js&lt;/code&gt; file once the native projects are compiled. Then, it won’t be necessary to recompile the native projects for the remainder of this tutorial. We sped up the compilation a little and here we go — our React Native app is running on the iOS simulator. Now, we do the same for the Android version and wait until the project is compiled.&lt;/p&gt;
&lt;p&gt;Now, the demo project launched on both platforms as we can see on the right on the iOS simulator at the top, and on the Android emulator at the bottom of the screen.&lt;/p&gt;
&lt;p&gt;We’ve decided that we want to start our photo editor by pressing a button. So next, we’re going to actually customize our React Native app by adding this button. Therefore, we open the &lt;code&gt;App.js&lt;/code&gt; file and import the &lt;code&gt;Button&lt;/code&gt; component in order to create a button with the title “Edit a sample image”. For now, we leave the &lt;code&gt;onPress&lt;/code&gt; function empty.&lt;/p&gt;
&lt;p&gt;We save the &lt;code&gt;App.js&lt;/code&gt; file to trigger a refresh of the running apps and immediately see the result on the right. The new button appears in both, the iOS and the Android app.&lt;/p&gt;
&lt;p&gt;Now we create a second button with the title “Edit a sample video”. This will respectively start our video editor. And again, we save the &lt;code&gt;App.js&lt;/code&gt; file and see the second button appear on the right side.&lt;/p&gt;
&lt;p&gt;Next, we are going to add the code that actually opens our editors when we press the buttons. Visual Studio Code automatically imported the respective React Native PhotoEditor SDK module for us at the very top of the file, while writing the code that makes use of our SDK. We do the same for the VideoEditor SDK. We use the &lt;code&gt;require&lt;/code&gt; function to make static assets available to our app. Here, we “require” our sample image and our sample video that we copied to the app’s folder in the beginning and pass them as the first argument to our &lt;code&gt;openEditor&lt;/code&gt; functions. The first argument can also be a regular URI.&lt;/p&gt;
&lt;p&gt;We save the &lt;code&gt;App.js&lt;/code&gt; file again and now we can click the buttons to start our photo editor or video editor. There we go! We still see a watermark here. The reason for this watermark is that we haven’t unlocked our SDKs so far which we will do next.&lt;/p&gt;
&lt;p&gt;We unlock both products with our licenses to get rid of the watermark. If not unlocked, the watermark will be on both the image and video previews as well as on the exported images and videos. To unlock the products, we use the &lt;code&gt;unlockWithLicense&lt;/code&gt; function of each SDK. In total, we need four license files, one license file for each product and platform combination. The license files should be named &lt;code&gt;pesdk_license&lt;/code&gt; and &lt;code&gt;vesdk_license&lt;/code&gt; with platform-specific extensions &lt;code&gt;.ios.json&lt;/code&gt; and &lt;code&gt;.android.json&lt;/code&gt;. React Native will then automatically pick the right file for the corresponding platform. After this, the watermarks will be removed for PhotoEditor SDK and VideoEditor SDK on both platforms. And now you can also see it in the simulator —  no watermarks anymore.&lt;/p&gt;
&lt;p&gt;In the next step, we’re going to change the configuration of the editors. If no changes are made to the configuration, our default stickers are available with the editor. To customize them, we need to import the &lt;code&gt;Configuration&lt;/code&gt; from either the PhotoEditor SDK or VideoEditor SDK. The configurations are compatible between both products. So, for this tutorial, we decided to use the VideoEditor SDK configuration which we are now using by adding &lt;code&gt;Configuration&lt;/code&gt; to the &lt;code&gt;react-native-videoeditorsdk&lt;/code&gt; imports. We decided that we want to add custom stickers to our editors. Therefore we define a non-default configuration to the sticker tool.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const configuration: Configuration = {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  sticker: {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To customize the sticker assets, we need to define the sticker “categories” array. Here, we define a new category and name the identifier &lt;code&gt;demo_sticker_category&lt;/code&gt;. These asset identifiers must always be unique. Next, we set a name for the category and we name it “Logos”.&lt;/p&gt;
&lt;p&gt;Each category also requires a thumbnail image to be displayed in the editor. For the thumbnail, we use the React logo that we added to the folder of our app at the very beginning. Next, we define the items for this new sticker category. These items are the actual stickers that we can apply to the edited image or video. We now create a new sticker for the React logo. Therefore, we call the identifier &lt;code&gt;demo_sticker_react&lt;/code&gt; and name it “React”. These sticker names won’t appear in the UI, but they are used for accessibility. Now, we need to define the actual image that should be used for that sticker. Here we use the React image again.&lt;/p&gt;
&lt;p&gt;To create a second sticker, we can now copy and paste the code of the first sticker. We create a sticker with our img.ly logo and rename the identifier of the pasted code to &lt;code&gt;demo_sticker_imgly&lt;/code&gt;. Accordingly, we set the name to “img.ly” and change the file to &lt;code&gt;imgly.png&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In addition, we want to specify a non-default tint mode for our second sticker by using &lt;code&gt;tintMode: TintMode.SOLID&lt;/code&gt; which enables us to change the color of the sticker. The &lt;code&gt;TintMode&lt;/code&gt; type is automatically added to the VideoEditor SDK imports for us by Visual Studio Code. Now, that we completed our configuration, we need to pass it as the second argument to the &lt;code&gt;openEditor&lt;/code&gt; functions in order to take effect.&lt;/p&gt;
&lt;p&gt;We save the &lt;code&gt;App.js&lt;/code&gt; file again to refresh the running apps and we can see the result live after starting a new editing session. Please note that you cannot alter the configuration of a running editor instance. You always need to start a new editing session to see configuration changes.&lt;/p&gt;
&lt;p&gt;We want to use another feature of our SDKs which is called serialization. With the serialization feature, we can capture all image and video editing operations that are applied in the editor and export them. This allows us to import the editing operations in later sessions and continue editing. The serializations are compatible between both products as well. The input serialization is the third parameter of the &lt;code&gt;openEditor&lt;/code&gt; functions of our SDKs and the output serialization is optionally part of their result type.&lt;/p&gt;
&lt;p&gt;First, we check if the result is “null”. This is the case when a user clicks the “Discard” button in the editor and thus does not export an image or video. If the result is not ”null”, we know that the user exported an image or video. Then we can assign the exported serialization to the previously defined global serialization variable which will then be input to the next editing session. We copy the code and add it to the video editor as well to enable the serialization function here too.&lt;/p&gt;
&lt;p&gt;Now, one thing is left to enable the actual serialization export in the configuration. The serialization export is disabled per default because not every user needs the serialization feature. Here, we enable it now and also change the export type to &lt;code&gt;object&lt;/code&gt;. By doing so, the result type of the editor will contain the serialization as an &lt;code&gt;object&lt;/code&gt;. Per default, the serialization is exported to a file and that file name is returned as part of the export result. Writing the serialization to a file is a reasonable default as serializations can be quite large, especially if large amounts of binary data for personal stickers are embedded.&lt;/p&gt;
&lt;p&gt;Now, we can run the app on the simulator and use all the parameters that we configured in this tutorial. First, we can add our custom stickers, both the React logo and the img.ly logo. Here we can also change the colors which we enabled with the tint mode.&lt;/p&gt;
&lt;p&gt;We can also use the text design tool to add a phrase to our image. Here we can pick different designs, so we’re trying a couple and place the text design fitting to the image and logo.&lt;/p&gt;
&lt;p&gt;Next, we export our image with the serialization. With the serialization function enabled, it is now possible to import the editing operations into our video editor. This allows us to keep on editing because the serialization is compatible between both products. So here we can add further words to our text design. We can also put filters on our video. For example, we can choose the peach duo tone and increase the contrast a little. And here we go! We successfully integrated PhotoEditor SDK and VideoEditor SDK into our React Native app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! To stay in the loop, subscribe to our&lt;/strong&gt; &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;&lt;strong&gt;Newsletter&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Julia</dc:creator><dc:creator>Alexander</dc:creator><media:content url="https://blog.img.ly/2020/05/_Integration_--1-.png" medium="image"/><category>App Development</category><category>Code</category><category>Developer Tools</category><category>Developer</category><category>Development</category><category>Image Editing</category><category>JavaScript</category><category>Mobile App Development</category><category>Photo Editing</category><category>React</category><category>React Native</category><category>Software Development</category><category>Tech</category><category>Tutorial</category><category>Video Editing</category><category>How-To</category><category>Company</category></item><item><title>Building the Creative Engine of the Digital World</title><link>https://img.ly/blog/building-the-creative-engine-of-the-world/</link><guid isPermaLink="true">https://img.ly/blog/building-the-creative-engine-of-the-world/</guid><description>With UBQ we are creating a platform for creative editing. We double down on our core belief that design can benefit a lot from technological advancements.</description><pubDate>Wed, 22 Apr 2020 12:42:48 GMT</pubDate><content:encoded>&lt;p&gt;Since releasing our first &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;Software Development Kit (&lt;strong&gt;SDK&lt;/strong&gt;) for photo editing&lt;/a&gt; in 2015, it quickly found its way into the hands of thousands of application developers. The demand allowed us to ramp up our efforts to expand our SDK to cover more platforms, add features and launch &lt;a href=&quot;https://img.ly/products/video-sdk/&quot;&gt;VideoEditor SDK&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At the same time, two very important things happened to us.&lt;/p&gt;
&lt;p&gt;First of all, we got experienced in building rendering engines, while learning in-depth about the requirements of processing photos for professional and semi-professional use-cases, on every platform. Our team leveled up big time, to a degree where we are confident there aren’t many teams with such a domain-specific expertise.&lt;/p&gt;
&lt;p&gt;Secondly, we gained critical insights into the visual creation process across many different industries, ranging from print services, social networks to marketing tools. In these processes, photo editing is only one part of the creative flow. There are topics like layouting, animation, generative design and many more, which go beyond the mere editing of an image, but are still essential to the business case.&lt;/p&gt;
&lt;p&gt;Our vision is to fill the obvious gap, providing a holistic solution that allows users to map entire creative flows into a tool. By leveraging our technology our customers will become a lot faster and more competitive in their markets.&lt;/p&gt;
&lt;p&gt;Six months ago, we decided to build the foundation for this vision and kicked off the development of &lt;strong&gt;UBQ,&lt;/strong&gt; an engine empowering a new generation of tools for creativity.&lt;/p&gt;
&lt;p&gt;It’s time to shed some light on our &lt;strong&gt;creative engine&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-creative-engine&quot;&gt;What is a Creative Engine?&lt;/h2&gt;
&lt;p&gt;Before we go forward, let’s establish what we mean by a &lt;strong&gt;creative engine&lt;/strong&gt;. In general, an engine is a platform that allows developers to create tools that support creators to perform specific tasks such as editing photos, creating visual appealing posters, or social media posts. From our perspective, a creative engine will ease the development of design tools for various niches and as such it must be easily adaptable for different usage and user requirements.&lt;/p&gt;
&lt;p&gt;To ensure that we don’t have to reinvent the wheel for each creative tool, a creative engine provides building blocks especially targeted to creative output. Some examples for these are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;High-quality image adjustment, editing, filtering, and manipulation.&lt;/li&gt;
&lt;li&gt;Automated layouting of design elements on a canvas.&lt;/li&gt;
&lt;li&gt;High-quality text rendering and layout algorithms.&lt;/li&gt;
&lt;li&gt;Support for various industry-standard assets, image, and video formats.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, these are only some of the basic features – there are far more advanced things that a creative engine can provide.&lt;/p&gt;
&lt;p&gt;On the one hand, this may include scripting tools to ease the generation of generative art or automation for tedious design tasks via automated image segmentation.&lt;/p&gt;
&lt;p&gt;On the other hand, there are also tools that not only help the individual creator but ease the cooperation and collaboration between multiple creators and editors. The details may vary, but it can be as simple as defining formats to exchange assets and designs between creators or even allow the simultaneous editing of their creation.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;introducing-the-ubq-creative-engine&quot;&gt;Introducing the UBQ Creative Engine&lt;/h2&gt;
&lt;p&gt;With &lt;strong&gt;UBQ&lt;/strong&gt; we strive to lay the groundwork for modern visual and communication design.&lt;/p&gt;
&lt;p&gt;Believing that these days, a good design tool has to be there for you every step of the way – it has to be &lt;strong&gt;ub&lt;/strong&gt;i&lt;strong&gt;q&lt;/strong&gt;uitous**.** Let us explain what this means for us.&lt;/p&gt;
&lt;h3 id=&quot;usable-on-any-platform&quot;&gt;Usable on any Platform&lt;/h3&gt;
&lt;p&gt;In this sense, we want it to be easily accessible to everyone, and as such the &lt;strong&gt;UBQ&lt;/strong&gt; stands for its availability across multiple platforms. It is conceptionally &lt;strong&gt;web-&lt;/strong&gt; and &lt;strong&gt;mobile-first&lt;/strong&gt; but is also available for classic operating systems such as macOS, Linux, and Windows, while being easily portable across &lt;strong&gt;various platforms&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;unified-rendering-generative-and-inference-engine&quot;&gt;Unified Rendering, Generative, and Inference Engine&lt;/h3&gt;
&lt;p&gt;While our previous products focused on processing a single image or video, our scope is greatly extended with &lt;strong&gt;UBQ&lt;/strong&gt;. Besides being able to process multiple images, with effects such as filters, adjustments, and so forth, in &lt;strong&gt;UBQ&lt;/strong&gt; we generalized images to a concept we call &lt;strong&gt;Design Blocks&lt;/strong&gt;. As such, static images are not the only source of data. It can be anything, even code that generates the visual input. In turn allowing that generative patterns and complex generative art is processed directly in the engine.&lt;/p&gt;
&lt;p&gt;We put a lot of work in the last years into modern tools for automation to help creators get rid of time-consuming tasks such as image segmentation, color adjustments, and many others. Our foundation for this stems from modern machine learning and neural network advancements. As such &lt;strong&gt;UBQ&lt;/strong&gt; supports executing neural networks and inferring information. Thus basically allowing any neural network to be part of a design generalized in what we call &lt;strong&gt;Compute Blocks*&lt;/strong&gt;.*&lt;/p&gt;
&lt;h3 id=&quot;collaboration--interact-in-realtime-with-other-creators&quot;&gt;Collaboration – Interact in Realtime with other Creators&lt;/h3&gt;
&lt;p&gt;The cooperation and collaboration between multiple creators are dear to our hearts. Therefore, we believe that creation is not only the effort of an individual but of many.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UBQ&lt;/strong&gt; will enable multiple &lt;strong&gt;creators&lt;/strong&gt; to &lt;strong&gt;work together&lt;/strong&gt;. Thus, allowing a design team to work together, share thoughts and assets instantly. Consequently, this constributes in accelerating their workflows and enriching their creative process.&lt;/p&gt;
&lt;p&gt;All that is reinforced due to the &lt;strong&gt;multiplayer&lt;/strong&gt; approach to design as well as &lt;strong&gt;reusable design elements&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Creators shall be enabled to work together &lt;strong&gt;locally as well as&lt;/strong&gt; &lt;strong&gt;remotely&lt;/strong&gt; and &lt;strong&gt;interact on the same design at the same time&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;cooperation--ease-exchange-of-designs-with-your-peers&quot;&gt;Cooperation – Ease Exchange of Designs with your Peers&lt;/h3&gt;
&lt;p&gt;Besides the collaborative aspect, &lt;strong&gt;UBQ&lt;/strong&gt; is built around the interaction of creators and non-designers – which we refer to as editors.&lt;/p&gt;
&lt;p&gt;Editors will be enabled to take existing designs and adapt it to their needs, by either just giving instant feedback to the creators, changing color palettes to fit the needs of their corporate design guidelines or changing images and text passages to make a design appealing to their audience.&lt;/p&gt;
&lt;h3 id=&quot;design-blocks--smart-and-reusable-design-element&quot;&gt;Design Blocks – Smart and Reusable Design Element&lt;/h3&gt;
&lt;p&gt;Another important design decision is the ability to exchange and reuse design elements between several projects. Therefore, &lt;strong&gt;UBQ&lt;/strong&gt;s foundation is built around the concept of &lt;strong&gt;smart design elements&lt;/strong&gt; that we call &lt;strong&gt;blocks&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Blocks&lt;/strong&gt; are reusable design elements that encapsulate complex and tedious design tasks.&lt;/p&gt;
&lt;p&gt;They currently come in two flavors, &lt;strong&gt;Design and Compute Blocks&lt;/strong&gt; ranging from simple predefined image adjustments settings to automated image segmentation as well as predefined adaptable design elements.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;A Design Block example showcasing automatic adaptable text layouts.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 5313px) 5313px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;5313&quot; height=&quot;3230&quot; src=&quot;https://img.ly/_astro/2004_img.ly_Engine_Blogpost_Visual_Designblock--1-_Z1bm8Xz.webp&quot; srcset=&quot;/_astro/2004_img.ly_Engine_Blogpost_Visual_Designblock--1-_2vz9rw.webp 640w, /_astro/2004_img.ly_Engine_Blogpost_Visual_Designblock--1-_1yLjN5.webp 750w, /_astro/2004_img.ly_Engine_Blogpost_Visual_Designblock--1-_1Ph6gR.webp 828w, /_astro/2004_img.ly_Engine_Blogpost_Visual_Designblock--1-_ZHWM9r.webp 1080w, /_astro/2004_img.ly_Engine_Blogpost_Visual_Designblock--1-_ZIwRrj.webp 1280w, /_astro/2004_img.ly_Engine_Blogpost_Visual_Designblock--1-_ZNRVRT.webp 1668w, /_astro/2004_img.ly_Engine_Blogpost_Visual_Designblock--1-_1YYsBS.webp 2048w, /_astro/2004_img.ly_Engine_Blogpost_Visual_Designblock--1-_Ze5EA7.webp 2560w, /_astro/2004_img.ly_Engine_Blogpost_Visual_Designblock--1-_Z1bm8Xz.webp 5313w&quot;&gt;&lt;/p&gt;
&lt;p&gt;From our point of view, creators should not have to spend their time with the recreation of preexisting designs. They should not have to watch tutorials just to get to the same output as someone else before. In &lt;strong&gt;UBQ&lt;/strong&gt;, these design blocks are the core foundation for building adaptable and interchangeable designs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Design blocks&lt;/strong&gt; respond to the available space, adapting their content accordingly and may include complex logical rules or just a combination of assets. &lt;strong&gt;UBQ&lt;/strong&gt; has all batteries included, providing a rich library of predefined blocks. By design, blocks can easily be built from a combination of other blocks or by using a scripting language similar to processing or p5.js to allow even generative and parametric design.&lt;/p&gt;
&lt;h3 id=&quot;final-words&quot;&gt;Final Words&lt;/h3&gt;
&lt;p&gt;As of April 2020, we are working on the &lt;strong&gt;UBQ&lt;/strong&gt; creative engine for almost half a year now incorporating knowledge from 5+ years developing our SDKs.&lt;/p&gt;
&lt;p&gt;We believe we can make an impact on how designs and design-tools are created, adapted and distributed. We are interested in your feedback and open to discussions, also we are looking for people who want to get involved as team members, early adopters or just want to play with it. Expect some demos during the end of summer showcasing what the &lt;strong&gt;UBQ&lt;/strong&gt; engine is capable of.&lt;/p&gt;
&lt;p&gt;Soon, we will get back to you with more exciting news.&lt;/p&gt;
&lt;p&gt;Stay tuned!&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;were-hiring&quot;&gt;We’re hiring!&lt;/h3&gt;
&lt;p&gt;Oh, and by the way, we’re hiring a Senior Frontend Developer at img.ly to reinforce our team and help shaping the frontend of our new design tool. Check out the job &lt;a href=&quot;https://img.ly/company/careers/#job-openings&quot;&gt;here&lt;/a&gt; and drop us a line, if you are interested.&lt;/p&gt;</content:encoded><dc:creator>Daniel</dc:creator><dc:creator>Eray</dc:creator><media:content url="https://blog.img.ly/2020/04/2004_img.ly_Engine_Blogpost_Visual_Header--1-.png" medium="image"/><category>Design</category><category>Tech</category><category>Software Development</category><category>SaaS</category><category>How-To</category><category>Company</category></item><item><title>Almost Getting Sherlocked by Apple’s Core Image Team</title><link>https://img.ly/blog/almost-getting-sherlocked-by-apples-core-image-team-76249c370320/</link><guid isPermaLink="true">https://img.ly/blog/almost-getting-sherlocked-by-apples-core-image-team-76249c370320/</guid><pubDate>Tue, 12 Jun 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;During each World Wide Developer Conference keynote, app developers all over the world are fearing their app may &lt;a href=&quot;https://www.urbandictionary.com/define.php?term=sherlocked&quot;&gt;get sherlocked&lt;/a&gt; by Apple. This has happened to flashlight apps when Apple introduced a simple toggle in the control center, to f.lux when Apple added exactly the same functionality to macOS and iOS, in parts to Dropbox with the launch of iCloud Drive and to many other developers. This year there was only one obvious of such things, the Workflow app which was acquired a few month ago and instantly turned into Siri Shortcuts by Apple. So no real ‘sherlocking’. But when we skimmed the newest APIs, release notes and session descriptions after the keynote, we found out that the technology behind our Portrait by img.ly app might have gotten sherlocked by the Core Image team. The reason is perfectly summarized in this tweet:&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;“Portrait Segmentation API&lt;br&gt;&lt;br&gt;A new API for third-party developers allows for the separation of layers in a photo, such as separating the background from the foreground.”&lt;br&gt;&lt;br&gt;Hi &lt;a href=&quot;https://twitter.com/halidecamera?ref_src=twsrc%5Etfw&quot;&gt;@halidecamera&lt;/a&gt;&lt;/p&gt;— Preshit Deorukhkar (@preshit) &lt;a href=&quot;https://twitter.com/preshit/status/1003722022466121730?ref_src=twsrc%5Etfw&quot;&gt;June 4, 2018&lt;/a&gt;&lt;/blockquote&gt;&lt;figcaption&gt;How we found out about the new Portrait Segmentation API.&lt;/figcaption&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Quick spoiler&lt;/em&gt;: While Core Images new API is impressive, our technology is still ahead in terms of general availability, required hardware and cross-platform compatibility. But let’s start from the beginning:&lt;/p&gt;
&lt;p&gt;We started working on automated image segmentation in 2016 and decided to focus on portraits in 2017. After spending most of the year on &lt;a href=&quot;https://img.ly/blog/when-creativity-meets-a-i-f48ee9a3612d/&quot;&gt;building a custom deep learning model&lt;/a&gt;, running hundreds of experiments and tweaking our post processing pipeline, we finally released the Portrait app in fall 2017. The app is able to generate portrait segmentations in real-time and allows the user to take a nice selfie, which is then automatically stylized using the segmentation mask and some post processing. This allows for sophisticated effects and got great reception all over the world. The app even got featured multiple times in many different countries. Recently we started looking into inferring depth, as we wanted to bring depth based features to our &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt;, but needed support on more iPhone devices, Android and especially required depth data for existing images.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 600px) 600px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;600&quot; height=&quot;720&quot; src=&quot;https://img.ly/_astro/1-gGBarteLQ3HLyjk_BOWRqg_1P3HFh.webp&quot; srcset=&quot;/_astro/1-gGBarteLQ3HLyjk_BOWRqg_1P3HFh.webp 600w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 400px) 400px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;400&quot; height=&quot;510&quot; src=&quot;https://img.ly/_astro/1-wIEvPF-CwcYYkguN3hKyrQ_3DVOn.webp&quot; srcset=&quot;/_astro/1-wIEvPF-CwcYYkguN3hKyrQ_3DVOn.webp 400w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Stylized selfies created using the Portrait app.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 400px) 400px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;400&quot; height=&quot;533&quot; src=&quot;https://img.ly/_astro/1-SbpOElYlADGGsomoQWo1UQ_Xq8J4.webp&quot; srcset=&quot;/_astro/1-SbpOElYlADGGsomoQWo1UQ_Xq8J4.webp 400w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Reading the tweet above, it looked like Apple just added the Portrait apps core functionality to the Core Image framework, making it available to all developers and users running or targeting iOS 12. A little worried about the new competition, we immediately looked into the docs and eagerly waited for the session on last Thursday to see if we’d soon need to find a new unique way of using technology to empower creativity.&lt;/p&gt;
&lt;p&gt;We found out that all you need to do in order to get a segmentation mask along with your image is toggle the following flags when requesting a photo capture from your &lt;code&gt;AVCapturePhotoOutput&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let settings: AVCapturePhotoSettings = AVCapturePhotoSettings()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;settings.isDepthDataDeliveryEnabled = true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;settings.isPortraitEffectsMatteDeliveryEnabled = true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After the photo is captured, you’re then able to extract the matte in the &lt;code&gt;didFinishProcessingPhoto&lt;/code&gt; callback. And voila, you now have the captured image, a depth map and the mask separating fore- and background available:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 400px) 400px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;400&quot; height=&quot;533&quot; src=&quot;https://img.ly/_astro/1-wNVP-7aqqOfyDpnWcKEc0w_ZrbWCi.webp&quot; srcset=&quot;/_astro/1-wNVP-7aqqOfyDpnWcKEc0w_ZrbWCi.webp 400w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 400px) 400px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;400&quot; height=&quot;533&quot; src=&quot;https://img.ly/_astro/1-k3bRAeLEIxoZ0otelumcdQ_Z1Vd9sB.webp&quot; srcset=&quot;/_astro/1-k3bRAeLEIxoZ0otelumcdQ_Z1Vd9sB.webp 400w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Input image, depth map and portrait matte generated by Core Image. (Source)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 400px) 400px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;400&quot; height=&quot;533&quot; src=&quot;https://img.ly/_astro/1-_ti3BobfUiGJmsb8MOkJvQ_Z1FhYlR.webp&quot; srcset=&quot;/_astro/1-_ti3BobfUiGJmsb8MOkJvQ_Z1FhYlR.webp 400w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Of course, we quickly spun up our own models and algorithms and compared the portrait matte, as well as the depth map to Apples results. As there is no way to rerun Apples matting algorithm for an existing image, we took one of Apples samples and compared their mask with results from our model:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 400px) 400px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;400&quot; height=&quot;533&quot; src=&quot;https://img.ly/_astro/1-wNVP-7aqqOfyDpnWcKEc0w_ZrbWCi.webp&quot; srcset=&quot;/_astro/1-wNVP-7aqqOfyDpnWcKEc0w_ZrbWCi.webp 400w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 225px) 225px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;225&quot; height=&quot;299&quot; src=&quot;https://img.ly/_astro/1-Tr1guJROO6OdL0BDhMlJ4g_ZGYUc4.webp&quot; srcset=&quot;/_astro/1-Tr1guJROO6OdL0BDhMlJ4g_ZGYUc4.webp 225w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Input image, depth map and portrait matte generated by our algorithms and models.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 400px) 400px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;400&quot; height=&quot;533&quot; src=&quot;https://img.ly/_astro/1-2Vj62Jh6cBafzw0kN1xVuA_2h3Jco.webp&quot; srcset=&quot;/_astro/1-2Vj62Jh6cBafzw0kN1xVuA_2h3Jco.webp 400w&quot;&gt;&lt;/p&gt;
&lt;p&gt;While it may not have been the greatest idea to pick Apples shiny developer example, Apples mask is a little more detailed and for this particular image our model missed parts of the neck on the left. But overall we were still very happy with our results, especially when considering, that everything was generated from the plain image and didn’t require any dedicated hardware like dual cameras or a TrueDepth sensor. We did of course expect a superior depth map from Apple, as we’re clearly lacking data and are still actively working on the depth model used to generate the image above. But Apples depthmap interestingly has some issues around the neck as well, despite their use of a dual camera system. And keep in mind, that our results were all processed on the mobile device, entirely based on the RGB data contained in the image, and could be repeated on an Android phone, older iOS devices and even your browser.&lt;/p&gt;
&lt;p&gt;When we wanted to try more samples, we quickly noticed the major limitations of Apples API: As the portrait matte capture is only available in combination with depth data, it’s limited to the iPhone 7 Plus, 8 Plus and iPhone X. And, most important to us, portrait mattes taken using the front camera are exclusively limited to the iPhone X and it’s TrueDepth sensor array. So for our Portrait app, switching to the new API would require us to ditch our live preview and go iPhone X and iOS 12 only. This was enough to calm our minds and we started thinking deeper about Apples technology:&lt;/p&gt;
&lt;p&gt;While the depth requirement is annoying, it also explains the higher quality of Apples predictions. Our model is trained to solve the individual problems of portrait matting and depth map inference as a whole, but Apple is able to focus on improving the edges, while the ‘rough’ foreground/background segmentation is handled by masking based on the depth of the face detected within the image. We might be able to combine both models as well, but that would make the segmentation model currently used in the Portrait app obsolete and would most certainly kill the real-time functionality.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;A stylized portrait of the sample image, created using our app.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 800px) 800px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;800&quot; height=&quot;1066&quot; src=&quot;https://img.ly/_astro/1-suYaswJDYtDiIFTs3w-aHQ_Z29DuwE.webp&quot; srcset=&quot;/_astro/1-suYaswJDYtDiIFTs3w-aHQ_ZJM4aC.webp 640w, /_astro/1-suYaswJDYtDiIFTs3w-aHQ_Z23jUhb.webp 750w, /_astro/1-suYaswJDYtDiIFTs3w-aHQ_Z29DuwE.webp 800w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Overall, Apples technology is, as almost always, pretty impressive and the portrait matte generation works flawless, but we can now say, that for the use within our app, we reach a good quality with our current algorithms and think that a real-time preview is more important to our users. For our &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; we’d happily use the high fidelity maps generated by iOS 12, but the hardware requirements are not yet suitable for SDK deployment. Our algorithms on the other hand, neither require a depth map, nor are we limited to processing after the image was captured, but can perform real-time inference on devices as old as the iPhone 6S. And the best of all: Once a TrueDepth camera is more common and everyone is running iOS 12, we can still use the Portrait Segmentation API and enjoy it’s simplicity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! To stay in the loop, subscribe to our &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;Newsletter&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Malte</dc:creator><media:content url="https://blog.img.ly/2020/03/1-EXAAKrECABcaDEs3BtfzgQ.jpeg" medium="image"/><category>Apple</category><category>WWDC</category><category>Core Image</category><category>iOS</category><category>Software Development</category><category>Tech</category><category>How-To</category><category>Insights</category></item><item><title>Rebuild or Report?</title><link>https://img.ly/blog/rebuild-or-report-211d6ac6e787/</link><guid isPermaLink="true">https://img.ly/blog/rebuild-or-report-211d6ac6e787/</guid><description>On helping yourself and others through precise Bug Reports </description><pubDate>Thu, 26 Apr 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Nowadays, every code project depends on various third party and additional libraries. Truth be told, we wouldn’t be able to create apps or webpages that fast if we would have to build everything from scratch. Typically, we add a library via our favored package manager and give it a go. We start using that specific library, read the docs, figure out how it works and use it for our purposes. Best case, that’s it. Unfortunately, in practice, it may turn out differently.&lt;/p&gt;
&lt;h2 id=&quot;make-no-mistake-mistakeshappen&quot;&gt;&lt;strong&gt;Make no Mistake, Mistakes happen&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;While we work with a specific library, we may encounter some roadblocks. Sometimes that’s due to missing or bad documentation. Or it might be due to our missing knowledge about some things. Anyway, sometimes, we eventually realize that it is not us, it was them all along. We found a mistake, a so-called bug.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A (software) bug is an error, flaw, failure or fault in a computer program or system that causes it to produce an incorrect or unexpected result, or to behave in unintended ways.&lt;/p&gt;
&lt;p&gt;— Wikipedia&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If we’re honest, our first reaction may be that we get angry at the library itself or the author, but, after acknowledging that we never wrote flawless or bug-free code, we look for a solution.&lt;/p&gt;
&lt;h2 id=&quot;we-found-a-bug-nowwhat&quot;&gt;&lt;strong&gt;We found a Bug! Now what?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Logically, some of us start to follow the most obvious path. We throw that library away and write a replacement ourselves…&lt;strong&gt;not&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Ok, honestly, we may think about that, but it in most cases that is neither the right nor the best way to go. Many authors put a lot of effort into creating software libraries and shared it with us. So, most undoubtedly, a lot of knowledge, work, testing and time has already gone into it.&lt;/p&gt;
&lt;p&gt;Therefore, there is a high chance that rewriting that library from scratch is the least favorable option.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So, what should we do?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Worst case scenario: We don’t do anything and leave it be. But this way it won’t get fixed, the authors won’t be aware of it, and others will encounter the same bug and spend unnecessary time on it.&lt;/p&gt;
&lt;p&gt;Best case scenario: We track down the bug, propose a solution to the authors and hope they will acknowledge and accept it. But sometimes we don’t have the means, options or time to fix the bug. Consequently, the second best way is telling the authors about the bug you found. But wait, before we file a bug we should ask ourselves the following question&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Are we running the latest version of the library?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;writing-good-bug-reports-helps-yourself-andothers&quot;&gt;&lt;strong&gt;Writing good Bug Reports helps Yourself and Others&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;We at &lt;a href=&quot;https://img.ly/&quot;&gt;IMG.LY&lt;/a&gt; provide a software library for photo editing. Thus, we are both library authors as well as users.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I would like to say that we don’t ever make mistakes, but truth be told, everyone does.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As you can imagine, providing photo editing capabilities results in a lot of code for rendering, UI, or other business logic. Although all the usual quality checks are in place, mistakes or unthought-of use-cases manifest in bugs, no matter what.&lt;/p&gt;
&lt;p&gt;Consequently, we get bug reports. Now, unfortunately, an awful lot of said bug reports only contain a single line that literally says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“XYZ does not work.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While, this is far better than not reporting it at all, it doesn’t help much in hunting down the bug.&lt;/p&gt;
&lt;p&gt;With bug reports like this, we spend a lot of our time on trying to reproduce the bug or asking for details on how to reproduce it. That time would be better spent on improving the libraries or helping other users. However, with just a little help we’d be able to find bugs faster and be more effective. So, what should a proper bug report look like? From our point of view, bug reports should be comprised out of the following things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One bug at a time,&lt;/li&gt;
&lt;li&gt;the context of the bug,&lt;/li&gt;
&lt;li&gt;a minimal runnable example, and&lt;/li&gt;
&lt;li&gt;the steps to reproduce the bug.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;one-bug-at-atime&quot;&gt;&lt;strong&gt;One bug at a time&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;In reality, one may encounter multiple bugs at the same time, and might wish to drop them at the library authors all at once. However, it is far better not to combine multiple problems even if they seem to be alike. So, one should always report each bug individually.&lt;/p&gt;
&lt;h2 id=&quot;describing-thecontext&quot;&gt;&lt;strong&gt;Describing the context&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Often forgotten when reporting bugs is to describe the context the bug occurs in. For us, a good bug report contains at least the following specifications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A description,&lt;/li&gt;
&lt;li&gt;the environment&lt;/li&gt;
&lt;li&gt;the actual and desired output, and&lt;/li&gt;
&lt;li&gt;attachments (e.g. Screenshots).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;description&quot;&gt;Description&lt;/h3&gt;
&lt;p&gt;First thing we need to know is what the bug is about. This includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A short meaningful &lt;em&gt;title&lt;/em&gt; of the bug (e.g. Exporting image after crop and rotate corrupts output), and&lt;/li&gt;
&lt;li&gt;a &lt;em&gt;description&lt;/em&gt; of the bug which explains the error in an understandable manner.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;environment&quot;&gt;Environment&lt;/h3&gt;
&lt;p&gt;Due to the nature of web and app development, software libraries can be used in many different environments. All of which have their own little differences and quirks. Thus, it is very helpful to include information about the environment, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the used &lt;em&gt;operating system’s&lt;/em&gt; version (e.g. iOS 10.1),&lt;/li&gt;
&lt;li&gt;the &lt;em&gt;device&lt;/em&gt; it is running on (e.g. iPhone 7 32GB),&lt;/li&gt;
&lt;li&gt;the &lt;em&gt;used browser or runtime environment&lt;/em&gt; and its version (e.g. Chrome 65.0.3325.181 (64-Bit) or Node.js 8.11.1 ), and not to forget&lt;/li&gt;
&lt;li&gt;the &lt;em&gt;name of the library&lt;/em&gt; and its version/variant (e.g. pesdk-html5 4.1.3).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;actual-output-and-expectedoutput&quot;&gt;Actual Output and Expected Output&lt;/h3&gt;
&lt;p&gt;Often, an experienced developer can classify the bug on the basis of the resulting error or error output only. Therefore, a useful bug report should provide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the &lt;em&gt;actual output&lt;/em&gt; that is is observed, and&lt;/li&gt;
&lt;li&gt;the &lt;em&gt;desired output&lt;/em&gt; that should be generated&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;attachments&quot;&gt;Attachments&lt;/h3&gt;
&lt;p&gt;Additionally, attachments are very helpful to pinpoint the bug. Helpful attachments can be but are not limited to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;error output or logs&lt;/em&gt; generated by the compiler and build system, console output, and stack trace, and whenever the bug results in something that is visual recognizable, and&lt;/li&gt;
&lt;li&gt;&lt;em&gt;screenshots&lt;/em&gt; showing the issue.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;reproducibility-iskey&quot;&gt;&lt;strong&gt;&lt;em&gt;Reproducibility&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;is key&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;While all aforementioned greatly assists to narrow down the context in which the bug appears there is one thing that helps even more:&lt;/p&gt;
&lt;p&gt;Even if it seems trivial to reproduce, a good bug report should come with a &lt;em&gt;minimal runnable example&lt;/em&gt; that produces the bug in question and a description of how to trigger it.&lt;/p&gt;
&lt;h3 id=&quot;providing-a-minimal-runningexample&quot;&gt;Providing a minimal running Example&lt;/h3&gt;
&lt;p&gt;So, one should strip away everything from the current code that isn’t necessary and keep it simple. The faster we and other library maintainers can track down an error, the sooner it’s going to be fixed.&lt;/p&gt;
&lt;p&gt;Also, probably it was already necessary to narrow down the issue so stripping it out into a minimal example is not too much work. If possible, the Gist should be provided via a GitHub repository or any other similar sharing system.&lt;/p&gt;
&lt;h3 id=&quot;keep-it-simple-to-trigger-the-bug-and-be-specific-aboutit&quot;&gt;&lt;strong&gt;Keep it simple to trigger the Bug and be specific about it&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;While having a minimal example is already lovely, sometimes it needs a bit more than just code to trigger the bug. Writing down precise instructions on how to trigger it is not always easy but being specific dramatically helps to get to it.&lt;/p&gt;
&lt;p&gt;That is why one should summarize the problem in brevity and with utmost precision.&lt;/p&gt;
&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;From our point of view these are the most important things one needs to consider when reporting a bug. Most certainly, the requirements change and need to be adapted for different types of software. We’d really like your feedback on this. So, if you have any comments and additions please let us know.&lt;/p&gt;
&lt;p&gt;*&lt;strong&gt;*Thanks for reading! To stay in the loop, subscribe to our&lt;/strong&gt; &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;&lt;strong&gt;Newsletter&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;.**&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Daniel</dc:creator><media:content url="https://blog.img.ly/2020/04/image-35.png" medium="image"/><category>Software Development</category><category>Bug Report</category><category>Support</category><category>Code</category><category>Software</category><category>Learning</category></item><item><title>Lightweight Image comparison with Rembrandt.JS</title><link>https://img.ly/blog/lightweight-image-comparison-with-rembrandt-js-b189276d03c4/</link><guid isPermaLink="true">https://img.ly/blog/lightweight-image-comparison-with-rembrandt-js-b189276d03c4/</guid><pubDate>Wed, 08 Mar 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://github.com/imgly/rembrandt&quot;&gt;Rembrandt.JS&lt;/a&gt; is a lightweight, client- and server-side image comparison library that we developed for our internal tests for the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt;. Since we’re adherents of test-driven development, we devote a lot of time and effort to writing robust tests and finding the right tools. A lot can go wrong in the process of refactoring and adding new features to our product. Since we want to make sure that our SDK performs at a top level 100% of the time, solid unit tests are crucial to our work. However, when it came to image comparison, we were not happy with the existing options. We tried to work with Blink-Diff from Yahoo, but since the tool isn’t maintained anymore and furthermore has a dependency on another unmaintained library, we chose to develop our own solution. Rembrandt.JS has no dependencies because it works client-side with the Canvas API of the browser and server-side with Node-Canvas.&lt;/p&gt;
&lt;p&gt;The primary function of Rembrandt is quite simple; &lt;strong&gt;it compares two pictures&lt;/strong&gt;. One image is the expected output of our SDK and the other one is the actual output of the PhotoEditor. After the comparison, Rembrandt outputs a numeric value between 0 and 1 indicating the percentual error rate. With an additional feature of Rembrandt, it is also possible to highlight the incorrect pixels on the image which helps to see instantly where the errors occur. Furthermore, Rembrandt.JS can compensate defective pixels that originate through compression artifacts. By comparing every pixel with its adjacent pixels with an adjustable tolerance, Rembrandt evaluates whether there’s a discrepancy or not. Said feature was critical to us since due to the sheer amount of features and filters in PhotoEditor SDK we’re always testing on a large scale. If we would’ve been using loss-free test material (like .png for example) the amount of disk space that the images would’ve been consuming would have been quite noticeable.&lt;/p&gt;
&lt;p&gt;Rembrandt.JS could also be leveraged for UI tests. With screenshots of both the design and the actual website or app, you could verify whether every object loads and behaves correctly. With the rendering option, you could quickly detect smallest discrepancies in even large sites or apps. Or, you could solve every spot the difference game within seconds.&lt;/p&gt;
&lt;p&gt;If Rembrandt is something potentially useful for your project, check out the Github repositories for &lt;a href=&quot;https://github.com/imgly/rembrandt&quot;&gt;Javascript&lt;/a&gt;, iOS and Android.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! To stay in the loop, subscribe to our &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;Newsletter&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Felix</dc:creator><media:content url="https://blog.img.ly/2020/04/1-nN176Cbkfr8ZA48HOY7JFA.png" medium="image"/><category>JavaScript</category><category>Software Development</category><category>Image Processing</category><category>iOS</category><category>Android</category><category>Tech</category><category>How-To</category><category>Insights</category></item></channel></rss>