<?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>Web Application – IMG.LY Blog</title><description>Posts tagged Web Application on the IMG.LY blog.</description><link>https://img.ly/blog/tag/web-application/</link><language>en-us</language><image><url>https://img.ly/apple-touch-icon.png</url><title>Web Application – IMG.LY Blog</title><link>https://img.ly/blog/tag/web-application/</link></image><atom:link href="https://img.ly/blog/tag/web-application/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>CE.SDK v1.15 Release Notes</title><link>https://img.ly/blog/creative-editor-sdk-v_1_15_0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v_1_15_0-release-notes/</guid><description>Generate cutout lines from selections, edit across all screen sizes, and more! </description><pubDate>Tue, 26 Sep 2023 08:14:20 GMT</pubDate><content:encoded>&lt;p&gt;Since &lt;a href=&quot;https://img.ly/blog/creative-editor-sdk-v_1_14_0-release-notes/&quot;&gt;our last release&lt;/a&gt;, we’ve been busy crafting new features and enhancements to empower your creative editing journey. We are thrilled to release CE.SDK v1.15!&lt;/p&gt;
&lt;p&gt;With this release, you can:&lt;/p&gt;
&lt;h2 id=&quot;create-accurate-cutouts-and-vectors&quot;&gt;Create Accurate Cutouts and Vectors&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Interfaces&lt;/strong&gt;: Engine, Editor&lt;br&gt;
&lt;strong&gt;Platforms:&lt;/strong&gt; All&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-15/cutoutlines-generate-from-selection.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Picture this: Your users are deep into a design project, and need to isolate an object from an image. Our latest feature simplifies this process. Whether it’s a logo, text, or a complex shape in an SVG - by clicking “Generate from Selection”, our system precisely detects object contours and effortlessly converts them into cutout lines. This means you can breeze through your design tasks without the hassle of manual tracing.&lt;br&gt;
Learn more about Cutouts in our &lt;a href=&quot;https://img.ly/docs/cesdk/js/stickers-and-shapes/create-cutout-384be3/&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;effortlessly-edit-across-all-screen-sizes&quot;&gt;E&lt;strong&gt;ffortlessly Edit Across All Screen Sizes&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Interfaces:&lt;/strong&gt; Editor&lt;br&gt;
&lt;strong&gt;Platforms:&lt;/strong&gt; Web&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-15/responsive.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;We’re excited to introduce a suite of responsive UI enhancements that ensure effortless editing on all devices, including smaller screens and mobile devices. Our editor adapts to limited screen space, automatically fits content to avoid cutoffs, hides button labels when space is tight, and ensures panels remain easy to use, even on small screens. Plus, you have complete control over when and where labels appear, allowing you to customize the interface to your liking.&lt;/p&gt;
&lt;h2 id=&quot;improved-android-video--audio-experience&quot;&gt;&lt;strong&gt;Improved Android Video &amp;#x26; Audio Experience&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Interfaces:&lt;/strong&gt; Editor, Engine&lt;br&gt;
&lt;strong&gt;Platforms:&lt;/strong&gt; Android&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Ensure smooth audio and video playback in your Android App.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/androidplayback_Z103jkD.webp&quot; srcset=&quot;/_astro/androidplayback_wFBav.webp 640w, /_astro/androidplayback_Z1h88WN.webp 750w, /_astro/androidplayback_RQAL1.webp 828w, /_astro/androidplayback_20VRmS.webp 1080w, /_astro/androidplayback_Z229hSl.webp 1280w, /_astro/androidplayback_Z103jkD.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Experience an enhanced video and audio experience on Android with our latest update. With our optimized codec performance, we’ve fine-tuned our tools to ensure your videos and audio play flawlessly. Enjoy &lt;strong&gt;smoother playback&lt;/strong&gt;, especially when navigating through your content. Also, no more delays or interruptions when seeking in your videos or audio files. Now, you can &lt;strong&gt;effortlessly skip to your desired moments&lt;/strong&gt; for uninterrupted enjoyment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thank you for reading! Never miss out on updates and&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;&lt;strong&gt;subscribe&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;to our newsletter.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2023/09/ce-sdk-1-15.jpg" medium="image"/><category>Release Notes</category><category>Print</category><category>Web-to-print</category><category>Cutouts</category><category>Android App Development</category><category>Web Application</category><category>Creative Editor</category></item><item><title>CE.SDK v1.12 Release</title><link>https://img.ly/blog/creative-editor-sdk-v_1_12_0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v_1_12_0-release-notes/</guid><description>Effortlessly auto-resize designs, enhance readability, achieve perfect alignment, and more! With CE.SDK v1.12.</description><pubDate>Thu, 15 Jun 2023 12:19:24 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://img.ly/blog/creative-editor-sdk-v_1_11_0-release-notes/&quot;&gt;In our last release&lt;/a&gt;, we introduced exciting features such as cutout stickers and shapes, letter case options, effortless layouting, and more.&lt;br&gt;
Now, get ready for CE.SDK v1.12, packed with more features and essential fixes to enhance your creative editing experience. Let’s explore what’s included:&lt;/p&gt;
&lt;h3 id=&quot;auto-resize-designs&quot;&gt;Auto-Resize Designs&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-12/template-page-resize.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Interfaces:&lt;/strong&gt; Advanced UI, API&lt;br&gt;
&lt;strong&gt;Platforms:&lt;/strong&gt; All&lt;br&gt;
Easily auto-resize or adapt your designs to different page sizes without the need for separate templates. Our intelligent solution adjusts the elements of your design while preserving full-size elements and optimizing image fill modes. This time-saving feature empowers you to reuse templates with ease, ensuring consistent and professional results. Whether it’s creating graphics for social media, presentations, or printed materials, our auto-resize feature simplifies the process.&lt;br&gt;
Learn more in our &lt;a href=&quot;https://img.ly/docs/cesdk/js/use-templates/apply-template-35c73e/&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;enhance-readability-with-paragraphs&quot;&gt;Enhance Readability with Paragraphs&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-12/paragraph.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Interfaces:&lt;/strong&gt; Default UI, Advanced UI, API&lt;br&gt;
&lt;strong&gt;Platforms:&lt;/strong&gt; All&lt;br&gt;
Achieve better text readability and design aesthetics by modifying the &lt;strong&gt;spacing between paragraphs&lt;/strong&gt;. This feature seamlessly integrates with our advanced text styling options, providing a comprehensive toolkit for creating visually stunning text-centric products, such as postcards and more. Learn more about paragraph spacing in our &lt;a href=&quot;https://img.ly/docs/cesdk/js/concepts/blocks-90241e/&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;avoid-cut-off-text&quot;&gt;Avoid Cut Off Text&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-12/cutoff.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Interfaces:&lt;/strong&gt; Default UI, Advanced UI, API&lt;br&gt;
&lt;strong&gt;Platforms:&lt;/strong&gt; All&lt;br&gt;
We’ve made a significant improvement related to &lt;strong&gt;text clipping&lt;/strong&gt; that ensures the top of the text is no longer cut off. This enhancement greatly improves the visual presentation and communication of your content, providing a seamless user experience.&lt;/p&gt;
&lt;h3 id=&quot;achieve-perfect-design-alignment&quot;&gt;Achieve Perfect Design Alignment&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;Move and snap elements for easy alignment.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/Snapping_1DFItd.webp&quot; srcset=&quot;/_astro/Snapping_2qTVjb.webp 640w, /_astro/Snapping_lAufs.webp 750w, /_astro/Snapping_stmkR.webp 828w, /_astro/Snapping_ZxkQ8u.webp 1080w, /_astro/Snapping_xEVam.webp 1280w, /_astro/Snapping_1DFItd.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Interfaces:&lt;/strong&gt; Default UI, Advanced UI, Touch UI&lt;br&gt;
&lt;strong&gt;Platforms:&lt;/strong&gt; All&lt;br&gt;
We’ve addressed the problem of &lt;strong&gt;snapping&lt;/strong&gt; to the wrong elements, ensuring a more accurate and reliable snapping experience. Precise alignment is crucial in design, and our continuous improvement efforts will further enhance snapping capabilities in future updates. Enjoy a seamless and frustration-free design process with our enhanced snapping precision.&lt;/p&gt;
&lt;h3 id=&quot;take-control-of-text-editing-with-precision&quot;&gt;Take Control of Text Editing with Precision&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;Retrieve the cursor range, and precisely insert content exactly where you need it.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/CursorRange-mouse_B8Js2.webp&quot; srcset=&quot;/_astro/CursorRange-mouse_1VKirs.webp 640w, /_astro/CursorRange-mouse_1x119s.webp 750w, /_astro/CursorRange-mouse_22qYUt.webp 828w, /_astro/CursorRange-mouse_Z1Kt3up.webp 1080w, /_astro/CursorRange-mouse_1WUTSh.webp 1280w, /_astro/CursorRange-mouse_B8Js2.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Interfaces:&lt;/strong&gt; API&lt;br&gt;
&lt;strong&gt;Platforms:&lt;/strong&gt; All&lt;br&gt;
&lt;code&gt;getTextCursorRange&lt;/code&gt; empowers you to access the current cursor range within the text editor. This feature opens up a world of possibilities, whether you’re building a custom user interface or implementing text-related automation. You can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Query the text cursor offset and the current selected text range.&lt;/li&gt;
&lt;li&gt;Automate text generation and insertion with precision.&lt;/li&gt;
&lt;li&gt;Seamlessly integrate this functionality into your workflow and unlock new possibilities for your text-centric projects.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Say goodbye to guesswork and welcome a more accurate and efficient text editing experience. Retrieve the current cursor range effortlessly, and precisely insert content exactly where you need it. Learn more in our &lt;a href=&quot;https://img.ly/docs/cesdk/js/text/edit-c5106b/&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;manage-templates-from-multiple-sources&quot;&gt;Manage Templates from Multiple Sources&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;Effortlessly browse and query a vast database of templates.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/templates_Z1pGJeP.webp&quot; srcset=&quot;/_astro/templates_Z1veuU9.webp 640w, /_astro/templates_Z28ksIf.webp 750w, /_astro/templates_Z1VPAwi.webp 828w, /_astro/templates_CaLDG.webp 1080w, /_astro/templates_28P5Bo.webp 1280w, /_astro/templates_Z1pGJeP.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Interfaces:&lt;/strong&gt; API&lt;br&gt;
&lt;strong&gt;Platforms:&lt;/strong&gt; All&lt;br&gt;
Introducing an improved template configuration feature that allows you to &lt;strong&gt;define asset sources for templates&lt;/strong&gt;. Now, you can effortlessly browse and query a vast database of templates, opening up a world of possibilities for your projects.&lt;/p&gt;
&lt;p&gt;By aligning the template configuration process with the existing asset management workflow, we’ve streamlined the experience, saving you time and effort. Moreover, you have the flexibility to define remote asset sources directly from &lt;strong&gt;databases&lt;/strong&gt;, expanding your template options even further.&lt;/p&gt;
&lt;p&gt;Enjoy a more streamlined workflow with CE.SDK v1.12, enhanced visual presentation, and the freedom to customize your designs with ease.&lt;br&gt;
&lt;a href=&quot;https://img.ly/docs/cesdk/&quot;&gt;Get started for free&lt;/a&gt; and bring creativity to your users!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;&lt;strong&gt;Subscribe to our Newsletter&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;to stay in the loop with the latest news and updates. Thank you for choosing IMG.LY&lt;/strong&gt;.&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2023/06/cesdk-imgly.jpg" medium="image"/><category>Release Notes</category><category>Creative Editor</category><category>Web Application</category><category>Mobile App Development</category></item><item><title>IMG.LY Partners with Getty Images to Revolutionize Image Integration</title><link>https://img.ly/blog/getty-images-partnership/</link><guid isPermaLink="true">https://img.ly/blog/getty-images-partnership/</guid><description>Discover Getty Images Library SDK - the easy-to-use toolkit that integrates image search, licensing, and editing for efficient content creation.</description><pubDate>Wed, 03 May 2023 13:07:31 GMT</pubDate><content:encoded>&lt;p&gt;We are thrilled to announce our latest partnership with Getty Images, the world’s leading provider of images, clips, and illustrations for visual communication. This collaboration has brought forth the &lt;a href=&quot;https://img.ly/partners/getty-images-sdk/&quot;&gt;Getty Images Library SDK&lt;/a&gt; - a bridge that seamlessly integrates image searching, licensing, and editing into your application. This revolutionary toolkit grants access to millions of stock images and enables your teams and users to browse, license, and edit images without ever leaving your application or website.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Empower your users to unleash their creativity with the Getty Images library&amp;#39;s powerful filtering tool. Let them find the perfect visual match for their content with ease.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 519px) 519px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;519&quot; height=&quot;526&quot; src=&quot;https://img.ly/_astro/image_D8Iyi.webp&quot; srcset=&quot;/_astro/image_D8Iyi.webp 519w&quot;&gt;&lt;/p&gt;
&lt;p&gt;By integrating Getty Images Library SDK into your application, you can save precious resources, streamline your workflows, and ensure legal compliance with a broad range of licensed stock images at your fingertips. Imagine not having to switch between different platforms to search, license, and edit images – all while staying in your application.&lt;/p&gt;
&lt;h3 id=&quot;empower-your-developers-and-users&quot;&gt;Empower your Developers and Users&lt;/h3&gt;
&lt;p&gt;At IMG.LY, we are passionate about delivering powerful editing tools with an intuitive interface, providing a creative outlet for users of all skill levels. Teaming up with Getty Images, we strive to provide a toolkit that empowers content creators to communicate their message in the most efficient and effective way possible, while upholding Getty Images’ commitment to quality and diversity in visual images.&lt;/p&gt;
&lt;p&gt;Designed by developers, for developers, the integration is quick and easy to set up. Once integrated, users can effortlessly browse through images, license the visuals they want, and make quick edits such as cropping or rotating images, all within the same application.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Explore the user-friendly interface of the Getty Images Library SDK editor, designed to seamlessly integrate image searching, licensing, and editing into your application.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 854px) 854px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;854&quot; height=&quot;520&quot; src=&quot;https://img.ly/_astro/getty-images-library-integration-app_ZADacA.webp&quot; srcset=&quot;/_astro/getty-images-library-integration-app_1YOhLQ.webp 640w, /_astro/getty-images-library-integration-app_xDMFe.webp 750w, /_astro/getty-images-library-integration-app_i0Kt7.webp 828w, /_astro/getty-images-library-integration-app_ZADacA.webp 854w&quot;&gt;&lt;/p&gt;
&lt;p&gt;We are excited to share this news and look forward to working with Getty Images to bring tools and empower creative expression!&lt;/p&gt;
&lt;h3 id=&quot;get-started&quot;&gt;Get Started&lt;/h3&gt;
&lt;p&gt;Learn more about the &lt;a href=&quot;https://img.ly/partners/getty-images-sdk/&quot;&gt;Getty Images Library SDK&lt;/a&gt;, &lt;a href=&quot;https://img.ly/docs/pesdk/web/guides/getty-library/getting-started/&quot;&gt;how to integrate it&lt;/a&gt;, or contact &lt;a href=&quot;https://www.gettyimages.de/api?utm_source=imgly&amp;#x26;utm_medium=referral&quot;&gt;Getty Images&lt;/a&gt; to obtain a license key.&lt;/p&gt;</content:encoded><dc:creator>Klaudia</dc:creator><media:content url="https://blog.img.ly/2023/05/partnership---getty.jpg" medium="image"/><category>App Development</category><category>Web Application</category><category>Web Development</category><category>Image Editing</category><category>Company</category></item><item><title>How To Build a Video Editor With Wasm in React</title><link>https://img.ly/blog/how-to-build-a-video-editor-with-wasm-in-react/</link><guid isPermaLink="true">https://img.ly/blog/how-to-build-a-video-editor-with-wasm-in-react/</guid><description>Use the ffmpeg.wasm library to build a React video editor that performs video processing directly in the browser.</description><pubDate>Mon, 16 Jan 2023 16:31:57 GMT</pubDate><content:encoded>&lt;p&gt;You may think that video processing can only be performed on a server, but this is not true. Thanks to WebAssembly (Wasm), you can run high-performance code written in C or C++ in your browser. This opens up a lot of possibilities and gives you the ability to build a client-side video editor. Let’s now learn how to create a Wasm-based video editor in React.&lt;/p&gt;
&lt;p&gt;Follow this tutorial and learn how to build the application below:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Upload and crop your video, and covert it as a GIF&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 671px) 671px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;671&quot; height=&quot;943&quot; src=&quot;https://img.ly/_astro/ffmpeg-video-editor-wasm_1i2zGn.webp&quot; srcset=&quot;/_astro/ffmpeg-video-editor-wasm_1FMujk.webp 640w, /_astro/ffmpeg-video-editor-wasm_1i2zGn.webp 671w&quot;&gt;&lt;/p&gt;
&lt;p&gt;This React video editor allows users to upload a video, select a portion of it, convert it to GIF, and download the resulting image file – all of this in your browser.&lt;/p&gt;
&lt;h2 id=&quot;what-is-webassembly&quot;&gt;What is WebAssembly?&lt;/h2&gt;
&lt;p&gt;WebAssembly (also called Wasm) is a new type of code that modern web browsers can run and understand. Specifically, Wasm provides new functionality to web development and brings significant performance benefits. This applies to both frontend and backend, since Wasm can be used by both clients and servers.&lt;/p&gt;
&lt;p&gt;Wasm code should not be written by hand. This is because Wasm is a binary instruction format. So, Wasm is designed to be a compilation target for source languages such as C, C++, Rust, and others. Learn more about Wasm on the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/WebAssembly&quot;&gt;WebAssembly MDN Web Docs page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Wasm provides a way to run code written in several languages on the Web at near-native speed. This means that WebAssembly forms can be easily imported and executed by a Web client or server application. In other words, you can use WebAssembly functions via JavaScript and achieve results that were not previously possible on a browser, especially in terms of performance.&lt;/p&gt;
&lt;h2 id=&quot;what-is-ffmpegwasm&quot;&gt;What is &lt;code&gt;ffmpeg.wasm&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;As stated on the &lt;a href=&quot;https://github.com/ffmpegwasm/ffmpeg.wasm&quot;&gt;GitHub page of the project&lt;/a&gt;, &lt;code&gt;ffmpeg.wasm&lt;/code&gt; is a WebAssembly and JavaScript port of FFmpeg. If you are not familiar with it, FFmpeg is an open-source software suite that includes several libraries and programs for managing audio, video, streams, and other media files. All of these tools can be invoked and executed via the &lt;code&gt;ffmpeg&lt;/code&gt; command-line instruction.&lt;/p&gt;
&lt;p&gt;Therefore, &lt;code&gt;ffmpeg.wasm&lt;/code&gt; enables video and audio processing in JavaScript-based web applications. In detail, it enables video and audio recording, format transcoding, video and audio editing, and video scaling. Even though it is a WebAssembly-based library, you can use it in your JavaScript code just like any other &lt;code&gt;npm&lt;/code&gt; library. This is the power of Wasm.&lt;/p&gt;
&lt;p&gt;Since it transpiles to Wasm, you can take advantage of &lt;code&gt;ffmpeg.wasm&lt;/code&gt; directly in your browser without performance concerns. This means that &lt;code&gt;ffmpeg.wasm&lt;/code&gt; opens the door to client-side audio and video processing. Let’s now learn how to use &lt;code&gt;ffmpeg.wasm&lt;/code&gt; to build a client-side video editor in React!&lt;/p&gt;
&lt;h2 id=&quot;building-a-video-editor-in-react-with-wasm&quot;&gt;Building a Video Editor in React with WASM&lt;/h2&gt;
&lt;p&gt;In this step-by-step tutorial, you will learn how to build a Wasm-based video editor in React with &lt;code&gt;ffmpeg.wasm&lt;/code&gt;. This React video editor application will allow you to upload a video, trim it, and convert it to GIF in the browser, without using any backend functionality or external API.&lt;/p&gt;
&lt;p&gt;Clone the &lt;a href=&quot;https://github.com/imgly/video-editor-wasm-react&quot;&gt;GitHub repository that supports the tutorial&lt;/a&gt; and try the video editor 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/video-editor-wasm-react&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cd video-editor-wasm-react&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm run start&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s waste no more time and see how to build a video editor application in React.&lt;/p&gt;
&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;This is the list of libraries the video editor application you are about to build depends on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://nodejs.org/en/download/package-manager&quot;&gt;Node.js and npm 8+ and higher&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/react/blob/main/CHANGELOG.md#1820-june-14-2022&quot;&gt;React 18.2+&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[ffmpeg.wasm](https://github.com/ffmpegwasm/ffmpeg.wasm)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[antd](https://www.npmjs.com/package/antd)&lt;/code&gt; &gt;= 4.3&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[video-react](https://www.npmjs.com/package/video-react)&lt;/code&gt; &gt;= 0.15&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;antd&lt;/code&gt; is one of the most popular UI libraries for React. In this tutorial, its &lt;code&gt;[Slider](https://ant.design/components/slider/)&lt;/code&gt; component will be used to implement the video cutting feature. So, any other UI library including a slider component with a &lt;code&gt;range&lt;/code&gt; option will do.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;video-react&lt;/code&gt; is one of the most advanced and reliable HTML5 video players for React. You can replace it with any other React video player library.&lt;/p&gt;
&lt;h3 id=&quot;initializing-a-react-project&quot;&gt;Initializing a React Project&lt;/h3&gt;
&lt;p&gt;Let’s initialize a new &lt;a href=&quot;https://create-react-app.dev/docs/getting-started/&quot;&gt;Create React App&lt;/a&gt; React project called &lt;code&gt;video-editor-wasm-react&lt;/code&gt; with the command below:&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 video-editor-wasm-react&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your &lt;code&gt;video-editor-wasm-react&lt;/code&gt; directory should now contain the following files:&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;video-editor-wasm-react&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;Enter the &lt;code&gt;video-editor-wasm-react&lt;/code&gt; folder in your terminal and launch a React local server with the commands below:&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 video-editor-wasm-react&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;Now, visit the &lt;code&gt;[http://localhost:3000/](http://localhost:3000/)&lt;/code&gt; page in your browser, and you should be seeing the default Create React App screen.&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;p&gt;Before installing &lt;code&gt;ffmpeg.wasm&lt;/code&gt;, you need to get your server ready. As stated &lt;a href=&quot;https://github.com/ffmpegwasm/ffmpeg.wasm#installation&quot;&gt;in the official documentation&lt;/a&gt;, &lt;code&gt;fmmpeg.wasm&lt;/code&gt; depends on &lt;code&gt;[SharedArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer)&lt;/code&gt;. This means that only browsers that support &lt;code&gt;SharedArrayBuffer&lt;/code&gt; can run &lt;code&gt;ffmpeg.wasm&lt;/code&gt;. You can find a complete list of all browsers &lt;a href=&quot;https://caniuse.com/sharedarraybuffer&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the most popular browsers, &lt;code&gt;SharedArrayBuffer&lt;/code&gt; is only available to &lt;a href=&quot;https://developer.chrome.com/blog/enabling-shared-array-buffer/#cross-origin-isolation&quot;&gt;cross-origin isolated&lt;/a&gt; webpages. To enable this, you need to set the following headers in your server hosting the React application:&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;Cross-Origin-Embedder-Policy: require-corp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Cross-Origin-Opener-Policy: same-origin&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In a Create-React-App application, you achieve this by creating a &lt;code&gt;src/setupProxy.js&lt;/code&gt; file and making sure it contains the following code:&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;module.exports = function (app) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    app.use(function (req, res, next) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        res.setHeader(&quot;Cross-Origin-Opener-Policy&quot;, &quot;same-origin&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        res.setHeader(&quot;Cross-Origin-Embedder-Policy&quot;, &quot;require-corp&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        next()&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;Your local development server is now ready to use &lt;code&gt;ffmpeg.wasm&lt;/code&gt;!&lt;/p&gt;
&lt;h3 id=&quot;installing-the-projects-dependencies&quot;&gt;Installing the Project’s Dependencies&lt;/h3&gt;
&lt;p&gt;It is now time to add the aforementioned required dependencies to your project.&lt;/p&gt;
&lt;p&gt;First, install &lt;code&gt;ffmpeg.wasm&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;npm install @ffmpeg/ffmpeg @ffmpeg/core&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let’s install &lt;a href=&quot;https://ant.design/&quot;&gt;Antd Design&lt;/a&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;npm install antd&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, it is time to install &lt;a href=&quot;https://video-react.js.org/&quot;&gt;Video-React&lt;/a&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;npm install video-react redux&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that &lt;code&gt;video-react&lt;/code&gt; also requires &lt;code&gt;[redux](https://www.npmjs.com/package/redux)&lt;/code&gt; to work.&lt;/p&gt;
&lt;p&gt;You have now everything you need to start developing your React video editor!&lt;/p&gt;
&lt;h3 id=&quot;uploading-a-video-to-the-video-editor&quot;&gt;Uploading a Video to the Video Editor&lt;/h3&gt;
&lt;p&gt;Create a &lt;code&gt;VideoUpload&lt;/code&gt; component as below:&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;// src/components/VideoUpload.js&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;import&lt;/span&gt;&lt;span&gt; { Button, Upload } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;antd&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; VideoUpload&lt;/span&gt;&lt;span&gt;({ &lt;/span&gt;&lt;span&gt;disabled&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;onChange&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;onRemove&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;  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;&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;Upload&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        disabled&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{disabled}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        beforeUpload&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;          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;        accept&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;video/*&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        onChange&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{(&lt;/span&gt;&lt;span&gt;info&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; (info.fileList &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; info.fileList.&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;            onChange&lt;/span&gt;&lt;span&gt;(info.fileList[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;].originFileObj);&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;        showUploadList&lt;/span&gt;&lt;span&gt;=&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;      &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;&gt;Upload Video&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;Upload&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        danger&lt;/span&gt;&lt;span&gt;=&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;        disabled&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;disabled}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&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;          onRemove&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&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;        Remove&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;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/&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; VideoUpload;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, this component uses the &lt;a href=&quot;https://ant.design/components/upload/&quot;&gt;Antd Design Upload&lt;/a&gt; component to allow users to upload a video file. Then, a “Remove” button gives users the ability to remove the uploaded video. Note that when the &lt;code&gt;Upload&lt;/code&gt; component is enabled, the “Remove” button is disabled and vice versa. This way, users can deal with only one video at a time.&lt;/p&gt;
&lt;h3 id=&quot;playing-a-video-in-react&quot;&gt;Playing a Video in React&lt;/h3&gt;
&lt;p&gt;You now need a &lt;code&gt;VideoPlayer&lt;/code&gt; component to play the uploaded video. You can build it as below:&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;// src/components/VideoPlayer.js&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;import&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  BigPlayButton,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ControlBar,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  LoadingSpinner,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Player,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  PlayToggle,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;video-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;video-react/dist/video-react.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; { useEffect, 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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; function&lt;/span&gt;&lt;span&gt; VideoPlayer&lt;/span&gt;&lt;span&gt;({&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&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  onPlayerChange&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;  onChange&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;  startTime&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&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;player&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setPlayer&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;playerState&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setPlayerState&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;  useEffect&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; (playerState) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      onChange&lt;/span&gt;&lt;span&gt;(playerState);&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;  }, [playerState]);&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;  useEffect&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;    onPlayerChange&lt;/span&gt;&lt;span&gt;(player);&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; (player) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      player.&lt;/span&gt;&lt;span&gt;subscribeToStateChange&lt;/span&gt;&lt;span&gt;(setPlayerState);&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;  }, [player]);&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;{&lt;/span&gt;&lt;span&gt;&apos;video-player&apos;&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;Player&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        ref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{(&lt;/span&gt;&lt;span&gt;player&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;          setPlayer&lt;/span&gt;&lt;span&gt;(player);&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;        startTime&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{startTime}&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;source&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{src} /&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;BigPlayButton&lt;/span&gt;&lt;span&gt; position&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;center&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;LoadingSpinner&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;ControlBar&lt;/span&gt;&lt;span&gt; autoHide&lt;/span&gt;&lt;span&gt;=&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;disableDefaultControls&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;true&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;PlayToggle&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;ControlBar&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;Player&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;  );&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 is a simple &lt;code&gt;video-react&lt;/code&gt;-based video player component, which only allows playing or stop the video file received with the &lt;code&gt;src&lt;/code&gt; prop.&lt;/p&gt;
&lt;h3 id=&quot;cutting-a-video-in-react&quot;&gt;Cutting a Video in React&lt;/h3&gt;
&lt;p&gt;Your goal is to allow users to select a portion of the video and watch it before converting it into GIF. Implement this with the logic below:&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; { Slider } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &quot;antd&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { sliderValueToVideoTime } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &quot;../utils/utils&quot;&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; VideoEditor&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;sliderValues&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setSliderValues&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;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;100&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;  // logic to handle videoFile, videoPlayer, and videoPlayerState...&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;  useEffect&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; min&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; sliderValues[&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;      // when the slider values are updated, updating the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      // video time&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      if&lt;/span&gt;&lt;span&gt; (min &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; undefined&lt;/span&gt;&lt;span&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; videoPlayerState &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; videoPlayer) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          videoPlayer.&lt;/span&gt;&lt;span&gt;seek&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sliderValueToVideoTime&lt;/span&gt;&lt;span&gt;(videoPlayerState.duration, min))&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;  }, [sliderValues])&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;  useEffect&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; (videoPlayer &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; videoPlayerState) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          // allowing users to watch only the portion of&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          // the video selected by the slider&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;min&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; sliderValues&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; minTime&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; sliderValueToVideoTime&lt;/span&gt;&lt;span&gt;(videoPlayerState.duration, min)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          const&lt;/span&gt;&lt;span&gt; maxTime&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; sliderValueToVideoTime&lt;/span&gt;&lt;span&gt;(videoPlayerState.duration, max)&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; (videoPlayerState.currentTime &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; minTime) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              videoPlayer.&lt;/span&gt;&lt;span&gt;seek&lt;/span&gt;&lt;span&gt;(minTime)&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;          if&lt;/span&gt;&lt;span&gt; (videoPlayerState.currentTime &lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; maxTime) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              // looping logic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              videoPlayer.&lt;/span&gt;&lt;span&gt;seek&lt;/span&gt;&lt;span&gt;(minTime)&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;  }, [videoPlayerState])&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;          // other video editor components...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          &amp;#x3C;&lt;/span&gt;&lt;span&gt;Slider&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            disabled&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;videoPlayerState}&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;{sliderValues}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            range&lt;/span&gt;&lt;span&gt;=&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;            onChange&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{(&lt;/span&gt;&lt;span&gt;values&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;              setSliderValues&lt;/span&gt;&lt;span&gt;(values)&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;            tooltip&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;              formatter: &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;            }}&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;This component equips users with the ability to select a portion of the uploaded video with the Antd Design &lt;code&gt;Slider&lt;/code&gt; component and watch it in loop, before converting it into a GIF file.&lt;/p&gt;
&lt;p&gt;Specifically, the logic here uses the &lt;code&gt;sliderValueToVideoTime()&lt;/code&gt; function below to transform the slider values into time coordinates:&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;// src/utils/utils.js&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 function sliderValueToVideoTime(duration, sliderValue) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return Math.round(duration * sliderValue / 100)&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;Note that the actual video processing slider will be performed by the next component through &lt;code&gt;ffmpeg&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;converting-a-video-to-gif-in-react&quot;&gt;Converting a Video to GIF in React&lt;/h3&gt;
&lt;p&gt;To convert a video to GIF, create a &lt;code&gt;VideoConversionButton&lt;/code&gt; component 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;// src/components/VideoConversionButton.js&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;import&lt;/span&gt;&lt;span&gt; { Button } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;antd&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; { fetchFile } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@ffmpeg/ffmpeg&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; { sliderValueToVideoTime } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;../utils/utils&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; VideoConversionButton&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  videoPlayerState&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  sliderValues&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  videoFile&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ffmpeg&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  onConversionStart&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;  onConversionEnd&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;  onGifCreated&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;}) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; convertToGif&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;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // starting the conversion process&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    onConversionStart&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; inputFileName&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &apos;gif.mp4&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; outputFileName&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &apos;output.gif&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;    // writing the video file to memory&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ffmpeg.&lt;/span&gt;&lt;span&gt;FS&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;writeFile&apos;&lt;/span&gt;&lt;span&gt;, inputFileName, &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; fetchFile&lt;/span&gt;&lt;span&gt;(videoFile));&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; [&lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; sliderValues;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; minTime&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; sliderValueToVideoTime&lt;/span&gt;&lt;span&gt;(videoPlayerState.duration, min);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; maxTime&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; sliderValueToVideoTime&lt;/span&gt;&lt;span&gt;(videoPlayerState.duration, max);&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;    // cutting the video and converting it to GIF with an FFMpeg command&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    await&lt;/span&gt;&lt;span&gt; ffmpeg.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &apos;-i&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      inputFileName,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &apos;-ss&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;minTime&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;      &apos;-to&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;maxTime&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;      &apos;-f&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &apos;gif&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      outputFileName&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;    // reading the resulting file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; data&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; ffmpeg.&lt;/span&gt;&lt;span&gt;FS&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;readFile&apos;&lt;/span&gt;&lt;span&gt;, outputFileName);&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;    // converting the GIF file created by FFmpeg to a valid image URL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; gifUrl&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; URL&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;createObjectURL&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      new&lt;/span&gt;&lt;span&gt; Blob&lt;/span&gt;&lt;span&gt;([data.buffer], { type: &lt;/span&gt;&lt;span&gt;&apos;image/gif&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;    onGifCreated&lt;/span&gt;&lt;span&gt;(gifUrl);&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;    // ending the conversion process&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    onConversionEnd&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;  };&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; &amp;#x3C;&lt;/span&gt;&lt;span&gt;Button&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; convertToGif&lt;/span&gt;&lt;span&gt;()}&gt;Convert to GIF&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;}&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; VideoConversionButton;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This component uses &lt;code&gt;ffmpeg.wasm&lt;/code&gt; to launch an &lt;code&gt;ffmpeg&lt;/code&gt; command. This command takes care of clipping the video to the time limits defined by &lt;code&gt;sliderValues&lt;/code&gt; and converting it to GIF. Note that all these operations are performed client-side in the browser.&lt;/p&gt;
&lt;p&gt;In detail, the &lt;code&gt;ffmpeg&lt;/code&gt; &lt;code&gt;-ss&lt;/code&gt; flag defines the time from which to start reading the video file, while &lt;code&gt;-to&lt;/code&gt; defines the end time. Then, the &lt;code&gt;gif&lt;/code&gt; option specifies that you want to produce a GIF file. Learn more about the flags and options offered by &lt;code&gt;ffmpeg&lt;/code&gt; &lt;a href=&quot;https://www.ffmpeg.org/ffmpeg.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The resulting GIF image is stored in the &lt;code&gt;output.gif&lt;/code&gt; memory file, loaded as a valid &lt;code&gt;&quot;image/gif&quot;&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Blob&quot;&gt;Blob&lt;/a&gt;, and finally converted to a valid image URL with the &lt;code&gt;[URL.createObjectURL()](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL_static)&lt;/code&gt; native function.&lt;/p&gt;
&lt;h3 id=&quot;putting-it-all-together&quot;&gt;Putting It All Together&lt;/h3&gt;
&lt;p&gt;Now, let’s see the full code of the &lt;code&gt;VideoEditor&lt;/code&gt; component:&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;// src/components/VideoEditor.js&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;import&lt;/span&gt;&lt;span&gt; { createFFmpeg } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@ffmpeg/ffmpeg&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; { useEffect, 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; { Slider, Spin } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;antd&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; { VideoPlayer } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./VideoPlayer&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; { sliderValueToVideoTime } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;../utils/utils&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; VideoUpload &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./VideoUpload&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; VideoConversionButton &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./VideoConversionButton&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; ffmpeg&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; createFFmpeg&lt;/span&gt;&lt;span&gt;({ log: &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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; VideoEditor&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;ffmpegLoaded&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setFFmpegLoaded&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;false&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;videoFile&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setVideoFile&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;videoPlayerState&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setVideoPlayerState&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;videoPlayer&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setVideoPlayer&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;gifUrl&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setGifUrl&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;sliderValues&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setSliderValues&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;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;100&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;processing&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setProcessing&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;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 class=&quot;line&quot;&gt;&lt;span&gt;  useEffect&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;    // loading ffmpeg on startup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ffmpeg.&lt;/span&gt;&lt;span&gt;load&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;then&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;      setFFmpegLoaded&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;    });&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;  useEffect&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; min&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; sliderValues[&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;    // when the slider values are updated, updating the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // video time&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (min &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; undefined&lt;/span&gt;&lt;span&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; videoPlayerState &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; videoPlayer) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      videoPlayer.&lt;/span&gt;&lt;span&gt;seek&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sliderValueToVideoTime&lt;/span&gt;&lt;span&gt;(videoPlayerState.duration, min));&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;  }, [sliderValues]);&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;  useEffect&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; (videoPlayer &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; videoPlayerState) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      // allowing users to watch only the portion of&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      // the video selected by the slider&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;min&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; sliderValues;&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; minTime&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; sliderValueToVideoTime&lt;/span&gt;&lt;span&gt;(videoPlayerState.duration, min);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; maxTime&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; sliderValueToVideoTime&lt;/span&gt;&lt;span&gt;(videoPlayerState.duration, max);&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; (videoPlayerState.currentTime &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; minTime) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        videoPlayer.&lt;/span&gt;&lt;span&gt;seek&lt;/span&gt;&lt;span&gt;(minTime);&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;      if&lt;/span&gt;&lt;span&gt; (videoPlayerState.currentTime &lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; maxTime) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // looping logic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        videoPlayer.&lt;/span&gt;&lt;span&gt;seek&lt;/span&gt;&lt;span&gt;(minTime);&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;  }, [videoPlayerState]);&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;  useEffect&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;    // when the current videoFile is removed,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // restoring the default state&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;videoFile) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      setVideoPlayerState&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;      setSliderValues&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;100&lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      setVideoPlayerState&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;      setGifUrl&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&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }, [videoFile]);&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;&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;Spin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        spinning&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{processing &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; !&lt;/span&gt;&lt;span&gt;ffmpegLoaded}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        tip&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;ffmpegLoaded &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &apos;Waiting for FFmpeg to load...&apos;&lt;/span&gt;&lt;span&gt; :&lt;/span&gt;&lt;span&gt; &apos;Processing...&apos;&lt;/span&gt;&lt;span&gt;}&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;          {videoFile &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;            &amp;#x3C;&lt;/span&gt;&lt;span&gt;VideoPlayer&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&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;URL&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;createObjectURL&lt;/span&gt;&lt;span&gt;(videoFile)}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              onPlayerChange&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{(&lt;/span&gt;&lt;span&gt;videoPlayer&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;                setVideoPlayer&lt;/span&gt;&lt;span&gt;(videoPlayer);&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;              onChange&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{(&lt;/span&gt;&lt;span&gt;videoPlayerState&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;                setVideoPlayerState&lt;/span&gt;&lt;span&gt;(videoPlayerState);&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;            /&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&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;h1&lt;/span&gt;&lt;span&gt;&gt;Upload a video&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;          )}&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; className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&apos;upload-div&apos;&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;VideoUpload&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            disabled&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;!!&lt;/span&gt;&lt;span&gt;videoFile}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            onChange&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{(&lt;/span&gt;&lt;span&gt;videoFile&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;              setVideoFile&lt;/span&gt;&lt;span&gt;(videoFile);&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;          /&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; className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&apos;slider-div&apos;&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;h3&lt;/span&gt;&lt;span&gt;&gt;Cut Video&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h3&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;Slider&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            disabled&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;videoPlayerState}&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;{sliderValues}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            range&lt;/span&gt;&lt;span&gt;=&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;            onChange&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{(&lt;/span&gt;&lt;span&gt;values&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;              setSliderValues&lt;/span&gt;&lt;span&gt;(values);&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;            tooltip&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;              formatter: &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;            }}&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; className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&apos;conversion-div&apos;&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;VideoConversionButton&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            onConversionStart&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;              setProcessing&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;            }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            onConversionEnd&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;              setProcessing&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;            }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            ffmpeg&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{ffmpeg}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            videoPlayerState&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{videoPlayerState}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            sliderValues&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{sliderValues}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            videoFile&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{videoFile}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            onGifCreated&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{(&lt;/span&gt;&lt;span&gt;girUrl&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;              setGifUrl&lt;/span&gt;&lt;span&gt;(girUrl);&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;          /&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;        {gifUrl &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; className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&apos;gif-div&apos;&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;h3&lt;/span&gt;&lt;span&gt;&gt;Resulting GIF&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h3&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{gifUrl}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&apos;gif&apos;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&apos;GIF file generated in the client side&apos;&lt;/span&gt;&lt;span&gt;}&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;a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{gifUrl}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              download&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&apos;test.gif&apos;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&apos;ant-btn ant-btn-default&apos;&lt;/span&gt;&lt;span&gt;}&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;              Download&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            &amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&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;        )}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span&gt;Spin&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;  );&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; VideoEditor;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since &lt;code&gt;ffmpeg&lt;/code&gt; takes time to load, you need to let users know that they need to wait a while before they can use the video editor component. Also, &lt;code&gt;ffmpeg&lt;/code&gt; operations take time to execute. This wait logic is implemented by using the Antd Design &lt;code&gt;[Spin](https://ant.design/components/spin/)&lt;/code&gt; component.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://paper-attachments.dropbox.com/s_C8CCC2508B96793E40906CD26AF4C92ACE1FB33CF33FE115E6ACE3BA49F22F52_1663925760548_chrome-capture-2022-8-23.gif&quot; alt=&quot;Preventing users from using the video editor component before ffmpeg is loaded&quot;&gt;&lt;/p&gt;
&lt;p&gt;Also, note that by passing the &lt;code&gt;log: true&lt;/code&gt; option to the &lt;code&gt;createFFmpeg()&lt;/code&gt; function, &lt;code&gt;ffmpeg&lt;/code&gt; will log useful debug info in the console. For example, after &lt;code&gt;ffmpeg&lt;/code&gt; gets loaded, you should be able to see the following lines in the browser console:&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;[info] use ffmpeg.wasm v0.11.5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;createFFmpeg.js:43 [info] load ffmpeg-core&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;createFFmpeg.js:43 [info] loading ffmpeg-core&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;createFFmpeg.js:43 [info] ffmpeg-core loaded&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What the &lt;code&gt;VideoComponent&lt;/code&gt; does is use all the components presented earlier to allow users to upload a video, select a portion through a slider, convert it to GIF, and download the resulting GIF file.&lt;/p&gt;
&lt;p&gt;Et voilà! You just learned how to build a client-side video editor in React without using any backend functionality!&lt;/p&gt;
&lt;h2 id=&quot;commercial-alternative&quot;&gt;Commercial Alternative&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://img.ly/docs/vesdk/flutter/getting-started/integration/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;VideoEditor SDK&lt;/a&gt; by &lt;a href=&quot;https://img.ly&quot;&gt;IMG.LY&lt;/a&gt; provides powerful video editing features, including cropping and trimming videos in your project. You will receive staples of video editing, including straightening videos, filters, brightness, color adjustments, and more.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Building a client-side video processing and editing application that uses only the browser possible. With WebAssembly, you can run high-performance code written in C or C++ in JavaScript. This allows you to perform complex operations at lightning speed directly in the browser, without having to delegate the work to a server.&lt;/p&gt;
&lt;p&gt;In this tutorial, you learned how to use &lt;code&gt;ffmpeg.wasm&lt;/code&gt; to build a video editor in React. &lt;code&gt;ffmpeg.wasm&lt;/code&gt; is the Wasm port of &lt;code&gt;ffmpeg&lt;/code&gt; and enables video and audio processing and editing directly in the browser. Using it to build a web application to upload a video, cut it, and export it to GIF requires only a few lines of code, and here we have seen how to implement such a video editing application.&lt;/p&gt;
&lt;p&gt;If your app goes beyond merely displaying video, and you want to allow your users to also edit video or create video based templates in the browser, explore our &lt;strong&gt;&lt;a href=&quot;https://img.ly/products/video-sdk/&quot;&gt;VideoEditor SDK&lt;/a&gt;&lt;/strong&gt; for the Web – a performant video editing solution based on WASM and WebCodecs. Try the editor for yourself in action with &lt;a href=&quot;https://img.ly/demos/video-ui/web/&quot;&gt;our showcases&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! Let us know what you think on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt;! 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>Antonello</dc:creator><media:content url="https://blog.img.ly/2023/01/video-editor-ffmpeg-wasm-1.jpg" medium="image"/><category>How-To</category><category>Video Editor</category><category>Web Development</category><category>Web Application</category><category>Video Editing</category><category>Tech</category><category>Tutorial</category></item><item><title>CE.SDK v1.9.0 Release</title><link>https://img.ly/blog/creative-editor-sdk-v_1_9_0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v_1_9_0-release-notes/</guid><description>Get ready to take your creative applications to the next level! This release is packed with exciting new features like video support for web, and more. </description><pubDate>Tue, 13 Dec 2022 08:21:50 GMT</pubDate><content:encoded>&lt;p&gt;We are happy to shine light on more new features since our CE.SDK &lt;a href=&quot;https://img.ly/blog/creative-editor-sdk-v_1_8_0-release-notes/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes&quot;&gt;v1.8.0&lt;/a&gt; Release. To build powerful features for your application, we are excited to hear your feedback and suggestions.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/cesdk-v-1-9-0-summary.mp4&quot; controls playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;CE.SDK v1.9.0 is out now, and it includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Video Support for Web&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Video Timelines&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Web Audio Support&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Native iOS Platform Support&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Swift Package&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Common Touch Gesture Support&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Apparel UI Showcase&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Spot and Named Colors&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;More Showcases&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;video-support-for-web&quot;&gt;Video Support for Web&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;Apply video fills and blocks to your scene!&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/Web_Video_Fills_16bvcF.webp&quot; srcset=&quot;/_astro/Web_Video_Fills_24RtxW.webp 640w, /_astro/Web_Video_Fills_g3IpD.webp 750w, /_astro/Web_Video_Fills_2q3t9s.webp 828w, /_astro/Web_Video_Fills_ZX0qSJ.webp 1080w, /_astro/Web_Video_Fills_45wDX.webp 1280w, /_astro/Web_Video_Fills_16bvcF.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;We now support adding and editing videos in your web browser. You can apply video fills, add blocks to the scene, and combine any other block to create videos and stories with CE.SDK. For now this feature is only supported in Chromium based browsers.&lt;/p&gt;
&lt;h3 id=&quot;video-timelines&quot;&gt;Video Timelines&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;Add, trim and edit your videos in a browser with CE.SDK.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/Video_Timelines_for_Consumer_UIs_and_Studio_UI_Z1mm045.webp&quot; srcset=&quot;/_astro/Video_Timelines_for_Consumer_UIs_and_Studio_UI_2gzhlA.webp 640w, /_astro/Video_Timelines_for_Consumer_UIs_and_Studio_UI_Z1Kh3x5.webp 750w, /_astro/Video_Timelines_for_Consumer_UIs_and_Studio_UI_Z18b1IC.webp 828w, /_astro/Video_Timelines_for_Consumer_UIs_and_Studio_UI_Z1TDUBc.webp 1080w, /_astro/Video_Timelines_for_Consumer_UIs_and_Studio_UI_T5B4k.webp 1280w, /_astro/Video_Timelines_for_Consumer_UIs_and_Studio_UI_Z1mm045.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Video Timelines and Trimming is coming to our Studio and Design UIs. This adds complete client-side video editing in the browser to our User Interfaces.&lt;/p&gt;
&lt;h3 id=&quot;web-audio-support&quot;&gt;Web Audio Support&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;Make it pop! Add your audio to a video.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/Web_Audio_Support_1WmHmG.webp&quot; srcset=&quot;/_astro/Web_Audio_Support_2tqhgn.webp 640w, /_astro/Web_Audio_Support_24FYXn.webp 750w, /_astro/Web_Audio_Support_Z2v5a4x.webp 828w, /_astro/Web_Audio_Support_Zpf5zK.webp 1080w, /_astro/Web_Audio_Support_Z1L2g10.webp 1280w, /_astro/Web_Audio_Support_1WmHmG.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;In addition to video support, we also introduce a &lt;a href=&quot;https://img.ly/docs/cesdk/js/create-video-c41a08/&quot;&gt;new block type for audio&lt;/a&gt;. You can use audio blocks to add audio files in MP3 and M4a formats to your scene. Like design blocks, you can add and modify them via the Block APIs and attach them to a scene. Note that audio blocks have no visual representation on the canvas, but only in the timeline.&lt;/p&gt;
&lt;p&gt;We dedicate our first implementation to the web. However, native mobile implementations will follow soon.&lt;/p&gt;
&lt;h2 id=&quot;native-ios-support&quot;&gt;Native iOS Support&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/iOS_Native_Support_1Yqh6a.webp&quot; srcset=&quot;/_astro/iOS_Native_Support_2lOmvf.webp 640w, /_astro/iOS_Native_Support_Z24AdxL.webp 750w, /_astro/iOS_Native_Support_Z5FePS.webp 828w, /_astro/iOS_Native_Support_Z1l5Xxb.webp 1080w, /_astro/iOS_Native_Support_jEDLu.webp 1280w, /_astro/iOS_Native_Support_1Yqh6a.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;With this feature, we support developing native iOS applications with our SDK. It comes with full &lt;strong&gt;Swift API&lt;/strong&gt; support and many integration examples.&lt;/p&gt;
&lt;p&gt;Additionally, CE.SDK v1.9.0 comes with &lt;strong&gt;Common Touch Gesture&lt;/strong&gt; to improve the editing experience on touchpads and mobile devices. That includes gestures like pinch, spread, drag, rotate, tap, and double tap.&lt;/p&gt;
&lt;h2 id=&quot;spot-and-named-colors&quot;&gt;Spot and Named Colors&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;Get print-ready with spot and named colors.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/spotnames_PcsQI.webp&quot; srcset=&quot;/_astro/spotnames_Z7J0F5.webp 640w, /_astro/spotnames_ZJOXtb.webp 750w, /_astro/spotnames_Zyl6he.webp 828w, /_astro/spotnames_Z2c793G.webp 1080w, /_astro/spotnames_ZFrP5Y.webp 1280w, /_astro/spotnames_PcsQI.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;With this release we are extending our support for print use cases by adding spot colors which enable the use of custom color mixes in the printing process. Consult the guides on &lt;a href=&quot;https://img.ly/docs/cesdk/js/colors/for-print/spot-c3a150/&quot;&gt;creating spot colors&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h2 id=&quot;more-showcases&quot;&gt;More Showcases&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;Explore our showcase site for creative tools perfect for your application.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/showcases_2sxc8Y.webp&quot; srcset=&quot;/_astro/showcases_Z2wCp9A.webp 640w, /_astro/showcases_1UsKQf.webp 750w, /_astro/showcases_26WD3c.webp 828w, /_astro/showcases_ZyLpLq.webp 1080w, /_astro/showcases_VRSbh.webp 1280w, /_astro/showcases_2sxc8Y.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;To explore CE.SDK capabilities firsthand, and to look into cases that might be comparable to yours, we provide more showcases on our website.&lt;/p&gt;
&lt;p&gt;Easily build a&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/demos/mobile-ui/web/&quot;&gt;Mobile UI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/demos/photo-ui/web/&quot;&gt;Photo UI&lt;/a&gt; for easy photo editing&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/demos/post-greeting-cards/web/&quot;&gt;Post &amp;#x26; Greeting Card UI&lt;/a&gt; to provide templates and easy adaption for print&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/demos/apparel-ui/web/&quot;&gt;Apparel UI&lt;/a&gt; to let users create fantastic apparel designs&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/imgly/cesdk-swift-examples/&quot;&gt;Apparel UI Mobile&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! Let us know what you think on&lt;/strong&gt; &lt;a href=&quot;https://x.com/imgly&quot;&gt;&lt;strong&gt;Twitter&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;! To stay in the loop, subscribe to our&lt;/strong&gt; &lt;a href=&quot;https://img.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>Neslihan</dc:creator><media:content url="https://blog.img.ly/2022/12/creative-editor-sdk-1.png" medium="image"/><category>Release Notes</category><category>Web Application</category><category>Web Development</category><category>Video Editing</category><category>Video Editor</category></item><item><title>How to Draw on Images in React Native</title><link>https://img.ly/blog/how-to-draw-on-images-in-react-native/</link><guid isPermaLink="true">https://img.ly/blog/how-to-draw-on-images-in-react-native/</guid><description>Learn how to draw on images in your React Native app – a great tool for annotations or fun image editing!</description><pubDate>Thu, 20 Oct 2022 13:09:38 GMT</pubDate><content:encoded>&lt;p&gt;It has become a commonplace requirement for many applications to manipulate images in one way or another before users are ready to share them. Therefore, it is crucial to foresee end-user needs while working on an app involving any interaction with graphical content. Image annotation can be necessary in various cases, such as simple inspection apps, bug reporting software, or machine learning applications relying on manual user input.&lt;/p&gt;
&lt;p&gt;In this article, we are shedding light on how to draw on images in React Native with the help of a simple HTML5 &lt;canvas&gt; element. We can summarize the drawing process with this checklist:&lt;/canvas&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Enable the characteristics of the &lt;canvas&gt; element by defining the height and width of your drawing area;&lt;/canvas&gt;&lt;/li&gt;
&lt;li&gt;Set the thickness and color of the brush instrument;&lt;/li&gt;
&lt;li&gt;Define at what moment your brush needs to be activated and when the drawing process will finish;&lt;/li&gt;
&lt;li&gt;Start drawing!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s first clear up the purpose of &lt;canvas&gt;.&lt;/canvas&gt;&lt;/p&gt;
&lt;h3 id=&quot;canvas-integration-for-images&quot;&gt;&lt;strong&gt;Canvas Integration for Images&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt; is an HTML component commonly used for creating and manipulating graphics, animation, and other visualization features. This element provides JavaScript APIs enabling image overlays and handling user input to make drawing possible. Moreover, while we have several methods for image manipulation with &lt;canvas&gt; (e.g., &lt;em&gt;boxes, circles, adding text, and other images&lt;/em&gt;), the most crucial attributes are always width and height.&lt;/canvas&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;&amp;#x3C;! -- HTML example of using canvas: --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;canvas id=&quot;myCanvas&quot; width=&quot;200&quot; height=&quot;100&quot;&gt;&amp;#x3C;/canvas&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might wonder why you cannot easily substitute &lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt; with some lines of HTML and CSS in your code. The answer lies on the surface: &lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt; creates a single flattened graphic rather than multiple components lying on top of each other (i.e., typically the output of HTML and CSS).&lt;/p&gt;
&lt;p&gt;Now, let’s see how to implement this powerful element in your React Native application:&lt;/p&gt;
&lt;iframe src=&quot;https://codesandbox.io/embed/img-ly-how-to-draw-on-images-in-rn-forked-hewo07?fontsize=14&amp;#x26;hidenavigation=1&amp;#x26;theme=dark&quot; title=&quot;IMG.LY_How_to_Draw_on_Images_in_RN (forked)&quot; allow=&quot;accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking&quot; sandbox=&quot;allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts&quot;&gt;&lt;/iframe&gt;
&lt;h3 id=&quot;drawing-with-canvas&quot;&gt;&lt;strong&gt;Drawing with Canvas&lt;/strong&gt;&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; React, { useEffect } &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;// CanvasContext here is used for drawing onto the canvas&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { useCanvas } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./CanvasContext&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;export&lt;/span&gt;&lt;span&gt; function&lt;/span&gt;&lt;span&gt; Canvas&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;canvasRef&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;prepareCanvas&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;startDrawing&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;finishDrawing&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;draw&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;    useCanvas&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;  useEffect&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;    prepareCanvas&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;  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;canvas&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      onMouseDown&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{startDrawing}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      onMouseUp&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{finishDrawing}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      onMouseMove&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{draw}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      ref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{canvasRef}&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;We start by enabling the characteristics of our &lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt; container, which are meant to render the &lt;code&gt;CanvasContext&lt;/code&gt; component presented below.&lt;/p&gt;
&lt;p&gt;The size of our future canvas is determined with &lt;code&gt;prepareCanvas&lt;/code&gt;, which includes both the height (&lt;code&gt;canvas.height&lt;/code&gt;) and width (&lt;code&gt;canvas.width&lt;/code&gt;) of the HTML element. For the drawing process itself, it is necessary to assign not only the thickness and color of the brush instrument (&lt;code&gt;context&lt;/code&gt;) but also take into account its activation (&lt;code&gt;startDrawing&lt;/code&gt; with &lt;code&gt;onMouseDown&lt;/code&gt;) and how the user can finish a stroke (&lt;code&gt;finishDrawing&lt;/code&gt; with &lt;code&gt;onMouseUp&lt;/code&gt;).&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; React, { useContext, useRef, 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;
&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;const&lt;/span&gt;&lt;span&gt; CanvasContext&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; React.&lt;/span&gt;&lt;span&gt;createContext&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;export&lt;/span&gt;&lt;span&gt; const&lt;/span&gt;&lt;span&gt; CanvasProvider&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; ({ &lt;/span&gt;&lt;span&gt;children&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;isDrawing&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setIsDrawing&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;false&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; canvasRef&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; useRef&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; contextRef&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; useRef&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // defining width &amp;#x26; height of the canvas&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; prepareCanvas&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;    const&lt;/span&gt;&lt;span&gt; canvas&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; canvasRef.current;&lt;/span&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; window.innerWidth &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;    canvas.height &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; window.innerHeight &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;    canvas.style.width &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; `${&lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;innerWidth&lt;/span&gt;&lt;span&gt;}px`&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    canvas.style.height &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; `${&lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;innerHeight&lt;/span&gt;&lt;span&gt;}px`&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;    // defining the thickness and colour of our brush&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;    context.&lt;/span&gt;&lt;span&gt;scale&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;2&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.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.lineWidth &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 class=&quot;line&quot;&gt;&lt;span&gt;    contextRef.current &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; context;&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; startDrawing&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; ({ &lt;/span&gt;&lt;span&gt;nativeEvent&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;offsetX&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;offsetY&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; nativeEvent;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    contextRef.current.&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;    contextRef.current.&lt;/span&gt;&lt;span&gt;moveTo&lt;/span&gt;&lt;span&gt;(offsetX, offsetY);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    setIsDrawing&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;  };&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; finishDrawing&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;    contextRef.current.&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;    setIsDrawing&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;  };&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; draw&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; ({ &lt;/span&gt;&lt;span&gt;nativeEvent&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; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;isDrawing) {&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&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; { &lt;/span&gt;&lt;span&gt;offsetX&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;offsetY&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; nativeEvent;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    contextRef.current.&lt;/span&gt;&lt;span&gt;lineTo&lt;/span&gt;&lt;span&gt;(offsetX, offsetY);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    contextRef.current.&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 class=&quot;line&quot;&gt;&lt;span&gt;  // Once the canvas is cleared it return to the default colour&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; clearCanvas&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;    const&lt;/span&gt;&lt;span&gt; canvas&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; canvasRef.current;&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;    context.fillStyle &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;white&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;fillRect&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;, canvas.width, canvas.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;  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;CanvasContext.Provider&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;{{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        canvasRef,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        contextRef,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        prepareCanvas,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        startDrawing,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        finishDrawing,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        clearCanvas,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        draw,&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;    &gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      {children}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span&gt;CanvasContext.Provider&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; const&lt;/span&gt;&lt;span&gt; useCanvas&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; useContext&lt;/span&gt;&lt;span&gt;(CanvasContext);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;drawbacks-of-the-canvas-method&quot;&gt;Drawbacks of the Canvas Method&lt;/h3&gt;
&lt;p&gt;Despite the canvas component offering powerful features, it is also quite demanding to use in its original form. For example, it may be problematic to adjust the properties of the brush to ensure that you can draw with smooth movements. Thus, implementing any solution for this issue would further increase the coding time and reduce the compatibility between different frameworks. Also, separately defining canvas and brush properties overcomplicates the code and requires advanced sources for successful integration into your React Native app. Although some temporary solutions aim to facilitate this process (such as &lt;a href=&quot;https://github.com/iddan/react-native-canvas&quot;&gt;react-native-image-draw&lt;/a&gt; or &lt;a href=&quot;https://github.com/terrylinla/react-native-sketch-canvas&quot;&gt;react-native-sketch-draw&lt;/a&gt;), none provides a permanent solution with clear documentation and guidance.&lt;/p&gt;
&lt;p&gt;If you wonder about a magic door to avoid this coding nightmare, you can always choose a commercial, all-in-one solution like &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt;. Implementing this SDK only takes a few lines of code, saves time and expenses in building an app, and you can expect great support from the developers. This way, you can skirt the hassle of coding an editor and instead focus on creating a great product.&lt;/p&gt;
&lt;h3 id=&quot;photoeditor-sdk-integration-for-drawing-on-your-images&quot;&gt;PhotoEditor SDK Integration for Drawing on Your Images&lt;/h3&gt;
&lt;p&gt;To get started integrating the PhotoEditor SDK in your React Native app, refer to &lt;a href=&quot;https://img.ly/docs/pesdk/web/guides/react-js/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;this guide&lt;/a&gt; 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;. You can then ensure comfortable image interaction for your users with the optimized &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;brush tool&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; as in the example below.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/pe-sdk-draw-image-react.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This article aimed to examine image editing instruments suitable for React Native framework. The HTML &lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt; element could have big potential as it provides fundamental tools for creating graphics or animation. However, its application is time-consuming and requires comprehensive knowledge in the field. Therefore, if you want to guarantee a smooth and easy integration of image drawing tools for your app, consider using advanced software like PhotoEditor SDK.&lt;/p&gt;</content:encoded><dc:creator>Natalia</dc:creator><media:content url="https://blog.img.ly/2022/10/photoeditor-sdk-ract-draw-on-images.png" medium="image"/><category>How-To</category><category>React</category><category>Web Development</category><category>Web Application</category><category>Image Editing</category><category>HTML5</category><category>React Native</category><category>Tech</category><category>Tutorial</category></item><item><title>CE.SDK v1.8.0 Release</title><link>https://img.ly/blog/creative-editor-sdk-v_1_8_0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v_1_8_0-release-notes/</guid><description>This release includes Node.js Platform Support, enhanced image control, and more!</description><pubDate>Tue, 18 Oct 2022 14:48:05 GMT</pubDate><content:encoded>&lt;p&gt;Since our CE.SDK &lt;a href=&quot;https://img.ly/blog/creative-editor-sdk-v_1_7_0-release-notes/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes&quot;&gt;v1.7.0&lt;/a&gt; Release, we implemented more highly requested features. Today, we shine light on them. As always, we are excited to hear your feedback and suggestions to build powerful features for your application. Our &lt;a href=&quot;https://roadmap.img.ly/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes&quot;&gt;Product Roadmap&lt;/a&gt; is the best place to make your case and be the first one to know of future releases.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/cesdk_v1-8-0-summary.mp4&quot; controls playsinline poster=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/cesdk-v-1-8-0_cover.jpg&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h2 id=&quot;cesdk-v180&quot;&gt;CE.SDK v1.8.0&lt;/h2&gt;
&lt;p&gt;This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Node.js Platform Support&lt;/li&gt;
&lt;li&gt;Common Touch Gesture Support for iOS&lt;/li&gt;
&lt;li&gt;Emoji Support ?&lt;/li&gt;
&lt;li&gt;Image Straightening in Advanced &amp;#x26; Default UI&lt;/li&gt;
&lt;li&gt;Multi Selection Rotation&lt;/li&gt;
&lt;li&gt;Outlook&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;nodejs-platform-support&quot;&gt;Node.js Platform Support&lt;/h3&gt;
&lt;p&gt;Since the release of our &lt;a href=&quot;https://www.npmjs.com/package/@cesdk/node&quot;&gt;Node Package&lt;/a&gt;, you can run a headless version of CE.SDK on the server. Now with this release, you have the full power of our Creative Engine at your fingertips, enabling you to programmatically generate images on the server or command line, render scenes created with the web SDK, and implement any creative automation workflow.&lt;/p&gt;
&lt;h3 id=&quot;common-touch-gesture-support-for-ios&quot;&gt;Common Touch Gesture Support for iOS&lt;/h3&gt;
&lt;p&gt;We know that a mobile presence and functionality is crucial for your application. If users find it difficult to complete a task or are frustrated by poor usability, they could quickly bid adieu to your app and ditch your hard work for a competitor. So with this update, we add common touch gesture to improve the editing experience on touchpads and mobile devices. This includes gestures like pinching, spreading, dragging, rotating, tap and double tapping.&lt;/p&gt;
&lt;h3 id=&quot;emoji-support&quot;&gt;Emoji Support&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/Emoticon_Support_Z14LJgA.webp&quot; srcset=&quot;/_astro/Emoticon_Support_Z2sV7lX.webp 640w, /_astro/Emoticon_Support_cm4FH.webp 750w, /_astro/Emoticon_Support_Z28eEIM.webp 828w, /_astro/Emoticon_Support_Z8mMhQ.webp 1080w, /_astro/Emoticon_Support_ZB4KLI.webp 1280w, /_astro/Emoticon_Support_Z14LJgA.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Sometimes, a party face says more than a thousand words ?: Emojis have become an essential part of modern communication and are omnipresent, especially on mobile platforms, but on desktop as well. We added Emoji rendering support for more fun edits and colorful messaging.&lt;/p&gt;
&lt;h3 id=&quot;image-straightening-in-advanced--default-ui&quot;&gt;Image Straightening in Advanced &amp;#x26; Default UI&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/Image-Straighten-in-Advanced---Desktop-UI_25ivqN.webp&quot; srcset=&quot;/_astro/Image-Straighten-in-Advanced---Desktop-UI_1Ig67Y.webp 640w, /_astro/Image-Straighten-in-Advanced---Desktop-UI_dKO90.webp 750w, /_astro/Image-Straighten-in-Advanced---Desktop-UI_ZvWPQk.webp 828w, /_astro/Image-Straighten-in-Advanced---Desktop-UI_1cOROz.webp 1080w, /_astro/Image-Straighten-in-Advanced---Desktop-UI_ZSwmLh.webp 1280w, /_astro/Image-Straighten-in-Advanced---Desktop-UI_25ivqN.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;While cropping your image, you can now also straighten, flip and rotate it! This feature gives you full control over your image in one place – available in both Advanced and Default User Interfaces.&lt;/p&gt;
&lt;h3 id=&quot;multi-selection-rotation&quot;&gt;Multi Selection Rotation&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/multi-selection-rotation-small_-1-_1wSd7Y.webp&quot; srcset=&quot;/_astro/multi-selection-rotation-small_-1-_p9U93.webp 640w, /_astro/multi-selection-rotation-small_-1-_KG4mg.webp 750w, /_astro/multi-selection-rotation-small_-1-_ZM2FD2.webp 828w, /_astro/multi-selection-rotation-small_-1-_ZF1y1u.webp 1080w, /_astro/multi-selection-rotation-small_-1-_qqk3f.webp 1280w, /_astro/multi-selection-rotation-small_-1-_1wSd7Y.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;For an accelerated design and editing process, the engine and user interface now allow rotating several blocks at once. To multi-rotate, simply hold your &lt;em&gt;Shift&lt;/em&gt; key while selecting the blocks you would like to move simultaneously.&lt;/p&gt;
&lt;h3 id=&quot;outlook&quot;&gt;Outlook&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Native iOS Platform Support:&lt;/strong&gt; With this feature, we will add support for developing native iOS applications with the SDK. It will come with full Swift API support and many integration examples.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Video for Web with CreativeEditor SDK:&lt;/strong&gt; Users will be able to edit and create fantastic videos based on templates in their browser with our SDK in your application soon! &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=4833b9cfb6&quot;&gt;Sign up&lt;/a&gt; for &lt;strong&gt;early access&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for reading! To stay in the loop, feel free to subscribe to our &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;Newsletter&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2022/10/creative-editor-sdk-design-editor-sdk-1-8-0-imgly.png" medium="image"/><category>Release Notes</category><category>Releases</category><category>Design Editor</category><category>Web Development</category><category>Web Application</category><category>iOS</category></item><item><title>How To Build a Video Player in JavaScript</title><link>https://img.ly/blog/how-to-build-video-player-in-javascript/</link><guid isPermaLink="true">https://img.ly/blog/how-to-build-video-player-in-javascript/</guid><description>Create your own JavaScript video player using simple methods for neat results!</description><pubDate>Tue, 27 Sep 2022 17:02:49 GMT</pubDate><content:encoded>&lt;p&gt;Probably a decade ago, it was impossible to play video or audio inside your browser without any third-party services such as Flash or Silverlight. You needed to install a plugin and only play your media while using it, so as you can see, it was very uncomfortable, with low speed and high delays. Nowadays, we have JavaScript with the new version of HTML5. With these new technologies and tools, we can stream our video much quicker, easier, and without any latency. To do it, you will need only a simple &lt;video&gt; tag and give a link to your video stored on your computer. The simple attribute &lt;em&gt;controls&lt;/em&gt; will give you a default video player built into the browser. It’s elementary and doesn’t have many features, so if you want to stream a video on your website in a more professional way using your video player, you’ll need to use JavaScript. And we’ll teach you how to do it in this article!&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;By the end of this guide, you’ll have something similar to this, so if you’re excited, keep reading and follow this tutorial step-by-step!&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;300&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;qBYNxxa&quot; data-user=&quot;paulknulst&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/paulknulst/pen/qBYNxxa&quot;&gt;How to build a video player in Javascript&lt;/a&gt; by Paul Knulst (&lt;a href=&quot;https://codepen.io/paulknulst&quot;&gt;@paulknulst&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;setting-up-the-project&quot;&gt;&lt;strong&gt;Setting Up the Project&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Assuming you are working with UNIX system (or have Git BASH on Windows) you can create all three files that are necessary to build a video player in JavaScript with this 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;mkdir video-player&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cd video-player&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;touch index.html script.js style.css&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To add a simple video player to our application, we have to add the following code to our &lt;code&gt;index.html&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;&amp;#x3C;!DOCTYPE html&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;html lang=&quot;en&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;head&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;meta charset=&quot;UTF-8&quot; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;title&gt;How to build a video player in Javascript&amp;#x3C;/title&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/head&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;body&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;div class=&quot;player&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            &amp;#x3C;video class=&quot;video&quot; controls&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                &amp;#x3C;source&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    src=&quot;&amp;#x3C;https://ftp.f1nalboss.de/data/imgly/videoplayer/testvideo.mp4&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    type=&quot;video/mp4&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;source&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    src=&quot;&amp;#x3C;https://ftp.f1nalboss.de/data/imgly/videoplayer/testvideo.mp4&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    type=&quot;video/webm&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;p&gt;No HTML5 video supported&amp;#x3C;/p&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            &amp;#x3C;/video&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;/div&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;script src=&quot;script.js&quot;&gt;&amp;#x3C;/script&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/body&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;/html&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within the above code, the &lt;code&gt;&amp;#x3C;video&gt;&lt;/code&gt; element uses a remote video from my FTP. You can either use my default video or add any video from your local computer by adjusting the &lt;code&gt;src&lt;/code&gt; attribute. HTML5 specification supports three different video formats, and the snippet used multiple &lt;code&gt;&amp;#x3C;source&gt;&lt;/code&gt; tags to make the videos available in MP4 and WebM. Furthermore, the &lt;code&gt;&amp;#x3C;p&gt;&lt;/code&gt; tag is used to display pre-defined content to user agents that do not support the &lt;code&gt;video&lt;/code&gt; element.&lt;/p&gt;
&lt;p&gt;The HTML5 &lt;code&gt;&amp;#x3C;video&gt;&lt;/code&gt; tag accepts several native attributes. For example, the &lt;code&gt;controls&lt;/code&gt; attribute displays the standard player controls when added or set to true. You can find out more about &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-controls&quot;&gt;all video attributes here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before continuing, you should apply all styles that are needed within this tutorial by populating your &lt;code&gt;style.css&lt;/code&gt; with all styles &lt;a href=&quot;https://codepen.io/paulknulst/pen/qBYNxxa&quot;&gt;from this CodePen&lt;/a&gt;. Save and open your &lt;code&gt;index.html&lt;/code&gt; and load it within the browser to see the embedded video player as seen below:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;This is what the embedded video player should look like in your index.html file.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 805px) 805px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;805&quot; height=&quot;544&quot; src=&quot;https://img.ly/_astro/build-video-player-with-javascript_ZBugbA.webp&quot; srcset=&quot;/_astro/build-video-player-with-javascript_Z2soYbu.webp 640w, /_astro/build-video-player-with-javascript_ZnTDYb.webp 750w, /_astro/build-video-player-with-javascript_ZBugbA.webp 805w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;customize-the-video-player-with-javascript&quot;&gt;&lt;strong&gt;Customize the Video Player With JavaScript&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;To customize the video player, you first have to remove the &lt;code&gt;controls&lt;/code&gt; attribute that displays &lt;code&gt;Play&lt;/code&gt;, &lt;code&gt;Pause&lt;/code&gt;, &lt;code&gt;Volume&lt;/code&gt;, etc. because you will implement your own custom controls within this tutorial. Now, check your browser, you will recognize that the controls are gone, and you cannot play the video anymore.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 815px) 815px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;815&quot; height=&quot;540&quot; src=&quot;https://img.ly/_astro/build-video-player-with-javascript_2_Z1Exl3w.webp&quot; srcset=&quot;/_astro/build-video-player-with-javascript_2_27jRL3.webp 640w, /_astro/build-video-player-with-javascript_2_Z2tYMIW.webp 750w, /_astro/build-video-player-with-javascript_2_Z1Exl3w.webp 815w&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;add-play-and-pause&quot;&gt;&lt;strong&gt;Add Play and Pause&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;To enable play and pause the video, you have to add a new button to the &lt;code&gt;index.html&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;&amp;#x3C;div class=&quot;controls&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;button&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            class=&quot;controls__btn playPauseBtn&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            title=&quot;Toggle Play&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;        ►&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/button&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;/div&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Afterward, open your &lt;code&gt;script.js&lt;/code&gt; and enable functionality by adding this code:&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 videoContainer = document.querySelector(&quot;.video-container&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const playPauseBtn = document.querySelector(&quot;.playPauseBtn&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function togglePlay() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if (videoContainer.paused || videoContainer.ended) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    videoContainer.play();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } else {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    videoContainer.pause();&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;function updatePlayBtn() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  playPauseBtn.innerHTML = videoContainer.paused ? &quot;►&quot; : &quot;❚❚&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;playPauseBtn.addEventListener(&quot;click&quot;, togglePlay);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;videoContainer.addEventListener(&quot;click&quot;, togglePlay);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;videoContainer.addEventListener(&quot;play&quot;, updatePlayBtn);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;videoContainer.addEventListener(&quot;pause&quot;, updatePlayBtn);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within this javascript code, first, the &lt;code&gt;video-container&lt;/code&gt; element and the &lt;code&gt;playPauseBtn&lt;/code&gt; is selected (Line 1 and 2). Then two functions are defined: &lt;code&gt;togglePlay()&lt;/code&gt; and &lt;code&gt;updatePlayBtn()&lt;/code&gt;. &lt;code&gt;togglePlay()&lt;/code&gt; is used to stop and start the video based on its actual state. &lt;code&gt;updatePlayBtn&lt;/code&gt; is used to switch between the Icon which is shown within the video player.&lt;/p&gt;
&lt;p&gt;In the last part of the snippet, a click event listener is added to the &lt;code&gt;playPauseBtn&lt;/code&gt; that executes the &lt;code&gt;togglePlay()&lt;/code&gt; function. Next, three click event listeners are added to the &lt;code&gt;videoContainer&lt;/code&gt; that executes &lt;code&gt;togglePlay()&lt;/code&gt; on mouse click and also executes &lt;code&gt;updatePlayBtn&lt;/code&gt; based on the video’s state.&lt;/p&gt;
&lt;p&gt;Now you can reload your &lt;code&gt;index.html&lt;/code&gt; and should be able to play and pause the video by either clicking the video or the button:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;You can now pause and play your video.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 803px) 803px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;803&quot; height=&quot;526&quot; src=&quot;https://img.ly/_astro/javascript-video-player_c0OvI.webp&quot; srcset=&quot;/_astro/javascript-video-player_Z2mALHv.webp 640w, /_astro/javascript-video-player_2aoUuJ.webp 750w, /_astro/javascript-video-player_c0OvI.webp 803w&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;add-progress-bar&quot;&gt;Add Progress Bar&lt;/h3&gt;
&lt;p&gt;Next, a progress bar will be implemented to show the current timestamp of the video when played. First, add a &lt;code&gt;div&lt;/code&gt; tag to the &lt;code&gt;index.html&lt;/code&gt; which will act as the progress bar:&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;&amp;#x3C;div class=&quot;controls&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;div class=&quot;progress&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    	&amp;#x3C;div class=&quot;progress__filled&quot;&gt;&amp;#x3C;/div&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/div&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;/div&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then open the &lt;code&gt;script.js&lt;/code&gt; and add the following snippet:&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 progress = document.querySelector(&quot;.progress&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const progressBar = document.querySelector(&quot;.progress__filled&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 handleProgress() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const progressPercentage = (videoContainer.currentTime / videoContainer.duration) * 100;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  progressBar.style.flexBasis = `${progressPercentage}%`;&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;function jump(e) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const position = (e.offsetX / progress.offsetWidth) * videoContainer.duration;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  videoContainer.currentTime = position;&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;videoContainer.addEventListener(&quot;timeupdate&quot;, handleProgress);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;progress.addEventListener(&quot;click&quot;, jump);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let mousedown = false;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;progress.addEventListener(&quot;mousedown&quot;, () =&gt; (mousedown = true));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;progress.addEventListener(&quot;mousemove&quot;, (e) =&gt; mousedown &amp;#x26;&amp;#x26; jump(e));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;progress.addEventListener(&quot;mouseup&quot;, () =&gt; (mousedown = false));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this snippet, the &lt;code&gt;progress&lt;/code&gt; container and the &lt;code&gt;progress__filled&lt;/code&gt; element will be selected, and two functions will be added: &lt;code&gt;handleProgress()&lt;/code&gt; and &lt;code&gt;jump(e)&lt;/code&gt;. &lt;code&gt;handleProgress()&lt;/code&gt; will be responsible for updating the progress bar. The &lt;code&gt;jump(e)&lt;/code&gt; function is used to enable clicking on the progress bar to jump to the position within the video.&lt;/p&gt;
&lt;p&gt;The last part contains all event listeners that are needed for the progress bar. The &lt;code&gt;handleProgress()&lt;/code&gt; will be called on every &lt;code&gt;timeupdate&lt;/code&gt; event. Also, clicking anywhere on the progress bar will call the &lt;code&gt;jump(e)&lt;/code&gt; method and the video will jump to the position. Additionally, &lt;code&gt;mousedown&lt;/code&gt;, &lt;code&gt;mousemove&lt;/code&gt;, and &lt;code&gt;mouseup&lt;/code&gt; will be used to enable &lt;em&gt;sliding&lt;/em&gt; through the video while holding the mouse button down on the progress bar.&lt;/p&gt;
&lt;h2 id=&quot;closing-notes&quot;&gt;&lt;strong&gt;Closing Notes&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Congratulations! If you followed the tutorial, you learned how to implement your own video player and add custom controls using JavaScript. Now, you can use &lt;a href=&quot;https://codepen.io/paulknulst/pen/qBYNxxa&quot;&gt;my CodePen&lt;/a&gt; and start implementing more controls like &lt;strong&gt;volume control&lt;/strong&gt;, &lt;strong&gt;keyboard shortcuts&lt;/strong&gt;, or &lt;strong&gt;skip controls&lt;/strong&gt; to build your own customized video player.&lt;/p&gt;
&lt;p&gt;If your app goes beyond merely displaying video, and you want to allow your users to also edit video or create video based templates in the browser, check out our &lt;a href=&quot;https://img.ly/products/video-for-web/&quot;&gt;Video Editor for Web&lt;/a&gt;!&lt;/p&gt;</content:encoded><dc:creator>Paul</dc:creator><media:content url="https://blog.img.ly/2022/09/video-player-javascript_tutorial.png" medium="image"/><category>How-To</category><category>Video Editor</category><category>Video Player</category><category>JavaScript</category><category>Web Development</category><category>Web Application</category><category>Tutorial</category></item><item><title>CE.SDK v1.7.0 Release and Roadmap</title><link>https://img.ly/blog/creative-editor-sdk-v_1_7_0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v_1_7_0-release-notes/</guid><description>Enjoy powerful Blur and Effects APIs, Shadows, Rotation of Groups and Hierarchies, and more. Our Product Roadmap will keep you up-to-date.</description><pubDate>Mon, 08 Aug 2022 12:07:47 GMT</pubDate><content:encoded>&lt;p&gt;Today, we are thrilled to highlight the best new features and changes in CE.SDK since our &lt;a href=&quot;https://img.ly/blog/creative-editor-sdk-v_1_6_0-release-notes/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes&quot;&gt;v1.6.0 Release&lt;/a&gt;. We have also published our &lt;a href=&quot;https://roadmap.img.ly/&quot;&gt;&lt;strong&gt;Product Roadmap for CE.SDK&lt;/strong&gt;&lt;/a&gt; to share our vision and direction for upcoming releases. This roadmap will let you know ahead of time what features you can expect from us and accelerate your product planning and development. We are excited to hear your feedback and suggestions to build more powerful features for your application!&lt;/p&gt;
&lt;h2 id=&quot;cesdk-v170&quot;&gt;CE.SDK v1.7.0&lt;/h2&gt;
&lt;p&gt;This release includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Auto Close Option in Web UI Configuration for Asset Libraries&lt;/li&gt;
&lt;li&gt;Custom URI Resolver&lt;/li&gt;
&lt;li&gt;Improving Mobile Touch Support&lt;/li&gt;
&lt;li&gt;Rotation of Groups and Hierarchies&lt;/li&gt;
&lt;li&gt;Custom Asset Source UI&lt;/li&gt;
&lt;li&gt;Disconnecting UI Variants from Roles&lt;/li&gt;
&lt;li&gt;Asset Source ApplyAsset API&lt;/li&gt;
&lt;li&gt;Blur API&lt;/li&gt;
&lt;li&gt;Effects API&lt;/li&gt;
&lt;li&gt;Image Fills API&lt;/li&gt;
&lt;li&gt;Image Straighten API&lt;/li&gt;
&lt;li&gt;Improved Group Selection and API&lt;/li&gt;
&lt;li&gt;Scopes API&lt;/li&gt;
&lt;li&gt;Shadows API&lt;/li&gt;
&lt;li&gt;Zoom API&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;auto-close-option-in-web-ui-configuration-for-asset-libraries&quot;&gt;Auto Close Option in Web UI Configuration for Asset Libraries&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;Should it stay or should it go? Have control over your library behavior with this update.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 740px) 740px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;740&quot; height=&quot;321&quot; src=&quot;https://img.ly/_astro/auto_close_lib_1W98pV.webp&quot; srcset=&quot;/_astro/auto_close_lib_1Jj7hj.webp 640w, /_astro/auto_close_lib_1W98pV.webp 740w&quot;&gt;&lt;/p&gt;
&lt;p&gt;There are no one-size-fits-all solutions for the behavior of asset library panels after an action was triggered. It depends on the use case and might vary between the panels and the available screen space. Thus, this is now configurable.&lt;/p&gt;
&lt;h3 id=&quot;custom-uri-resolver&quot;&gt;Custom URI Resolver&lt;/h3&gt;
&lt;p&gt;Custom URI Resolvers give you full control over how URIs should be resolved. You can create custom storage backends and even resolve different resolution images in different instances. Find more information in our &lt;a href=&quot;https://img.ly/docs/cesdk/js/open-the-editor/uri-resolver-36b624/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;improving-mobile-touch-support&quot;&gt;Improving Mobile Touch Support&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;Great design-to-go: improved mobile touch support.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/mobile_touch_Z2g42pt.webp&quot; srcset=&quot;/_astro/mobile_touch_ZJKt2L.webp 640w, /_astro/mobile_touch_Z1AgDJY.webp 750w, /_astro/mobile_touch_ZGdINX.webp 828w, /_astro/mobile_touch_Z2doG6d.webp 1080w, /_astro/mobile_touch_imcDC.webp 1280w, /_astro/mobile_touch_Z2g42pt.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Input handling was streamlined to handle both touch and mouse input efficiently. On touch devices, all handles maintain the recommended minimum size for touch input and are intelligently hidden to minimize overlap. Thanks to this mechanism, resizing and rotating are always a breeze.&lt;/p&gt;
&lt;h3 id=&quot;rotation-of-groups-and-hierarchies&quot;&gt;Rotation of Groups and Hierarchies&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;Simplify your design process by grouping elements and edit them altogether.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/rotation_of_groups_oxAW4.webp&quot; srcset=&quot;/_astro/rotation_of_groups_1NFy6r.webp 640w, /_astro/rotation_of_groups_2ss6Qm.webp 750w, /_astro/rotation_of_groups_ZCO3fG.webp 828w, /_astro/rotation_of_groups_29du7E.webp 1080w, /_astro/rotation_of_groups_Z1gd1mB.webp 1280w, /_astro/rotation_of_groups_oxAW4.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;The engine and user interface add options for rotating groups and design blocks with children. This allows changing the rotation of multiple elements at once quickly.&lt;/p&gt;
&lt;h3 id=&quot;custom-asset-source-ui&quot;&gt;Custom Asset Source UI&lt;/h3&gt;
&lt;p&gt;Instead of fixed entry points such as “Images,” “Stickers,” or “Shapes,” we want to enable custom and flexible entry points. Until today, we tied them to a block type, which is currently only relevant when users add an asset to the scene.&lt;/p&gt;
&lt;h3 id=&quot;moving-from-roles-to-abilities-disconnecting-ui-variants-from-roles&quot;&gt;Moving from Roles to Abilities: Disconnecting UI Variants from Roles&lt;/h3&gt;
&lt;p&gt;Before the change, the Advanced UI was tied to the creator role and the Default UI was tied to the Adopter Role. This is now no longer the case. The scopes and editing options are still controlled by the abilities of the current role.&lt;/p&gt;
&lt;h3 id=&quot;shadows-in-advanced-ui&quot;&gt;Shadows in Advanced UI&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/shadows_in_advanced_API_Z1FOoi7.webp&quot; srcset=&quot;/_astro/shadows_in_advanced_API_Z45be.webp 640w, /_astro/shadows_in_advanced_API_Zy5jRz.webp 750w, /_astro/shadows_in_advanced_API_egkMm.webp 828w, /_astro/shadows_in_advanced_API_Qlk0w.webp 1080w, /_astro/shadows_in_advanced_API_27QwKF.webp 1280w, /_astro/shadows_in_advanced_API_Z1FOoi7.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;With this update, we roll out the long-awaited ability to apply shadows to elements in our Advanced UI. Realistic drop shadow effects add more depth to your designs with a few clicks – no other editor is needed.&lt;/p&gt;
&lt;h3 id=&quot;blur-api&quot;&gt;Blur API&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/blur_API_Zfrq6A.webp&quot; srcset=&quot;/_astro/blur_API_1o848m.webp 640w, /_astro/blur_API_ZGbmUm.webp 750w, /_astro/blur_API_ZziuOW.webp 828w, /_astro/blur_API_Z2rt0Ii.webp 1080w, /_astro/blur_API_Z1lsdpr.webp 1280w, /_astro/blur_API_Zfrq6A.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;We introduce new APIs to modify the blur for images and other blocks. This change allows programmatic access to add blur effects in automation cases and when building your own UI.&lt;/p&gt;
&lt;h3 id=&quot;effects-api&quot;&gt;Effects API&lt;/h3&gt;
&lt;p&gt;We introduce new APIs to modify the effect stack for shapes and other blocks. This allows programmatic access to effects like adjustments, LUT filters, and many more.&lt;/p&gt;
&lt;h3 id=&quot;image-fills-api&quot;&gt;Image Fills API&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/image_fill_HmPh2.webp&quot; srcset=&quot;/_astro/image_fill_Z1IbTb.webp 640w, /_astro/image_fill_Z14KA8n.webp 750w, /_astro/image_fill_1rwknl.webp 828w, /_astro/image_fill_274TKn.webp 1080w, /_astro/image_fill_1pdRvH.webp 1280w, /_astro/image_fill_HmPh2.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Currently, you can only access and use images with the image block. However, pages or vector shapes could use more than just a color or gradient fill. Therefore, we broaden the concept of fills by allowing every block to have an image fill, e.g., pages. This change will enable building a more natural photo- or video-editor experience. &lt;a href=&quot;https://img.ly/docs/cesdk/js/fills/overview-3895ee/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes&quot;&gt;Documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;image-straighten-api&quot;&gt;Image Straighten API&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/image_straighten_Z1uLpF1.webp&quot; srcset=&quot;/_astro/image_straighten_Z29KtL5.webp 640w, /_astro/image_straighten_vwHgA.webp 750w, /_astro/image_straighten_Z1O428T.webp 828w, /_astro/image_straighten_ZymsGh.webp 1080w, /_astro/image_straighten_Z124rb9.webp 1280w, /_astro/image_straighten_Z1uLpF1.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;We are extending the Image Crop API to allow straightening, flipping, and rotating images during cropping. This change allows more fine-grained control.&lt;/p&gt;
&lt;h3 id=&quot;improved-group-selection-and-api&quot;&gt;Improved Group Selection and API&lt;/h3&gt;
&lt;p&gt;There are multiple options to select blocks inside a group:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;selecting a group first and with each click select one block deeper in the hierarchy&lt;/li&gt;
&lt;li&gt;selecting an element under the cursor directly, neglecting the hierarchy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Previously, we defaulted to different behaviors depending on the user’s role. Now, this is configurable and also customizable during runtime via APIs. Therefore, it’s up to the implementor how the selection should work.&lt;/p&gt;
&lt;h3 id=&quot;scopes-api&quot;&gt;Scopes API&lt;/h3&gt;
&lt;p&gt;We introduce new APIs to modify the scopes for blocks. This update allows programmatic access to change the scopes for users and allows control over the possible edits a user can make to a scene.&lt;/p&gt;
&lt;h3 id=&quot;shadows-api&quot;&gt;Shadows API&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/shadows_API_jTf6L.webp&quot; srcset=&quot;/_astro/shadows_API_PF3VL.webp 640w, /_astro/shadows_API_1R2ETk.webp 750w, /_astro/shadows_API_Gh7bU.webp 828w, /_astro/shadows_API_2a5X4g.webp 1080w, /_astro/shadows_API_Z1i5WNW.webp 1280w, /_astro/shadows_API_jTf6L.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;We introduce new APIs to modify shadows for images and other blocks. This change allows programmatic access to add shadow effects to be used in automation cases and when you build your UI.&lt;/p&gt;
&lt;h3 id=&quot;zoom-api&quot;&gt;Zoom API&lt;/h3&gt;
&lt;p&gt;We introduce new APIs to modify the current zoom level. This allows programmatic access to change the current zoom easily for automation use-cases or for building your own user interface.&lt;/p&gt;
&lt;h2 id=&quot;product-roadmap&quot;&gt;Product Roadmap&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/API_general_snkLp.webp&quot; srcset=&quot;/_astro/API_general_Z2gaGAs.webp 640w, /_astro/API_general_Z1eN5CT.webp 750w, /_astro/API_general_Z2pyDlj.webp 828w, /_astro/API_general_2iz3IT.webp 1080w, /_astro/API_general_Z19BR9j.webp 1280w, /_astro/API_general_snkLp.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;As mentioned before, we are happy to publish our &lt;a href=&quot;https://roadmap.img.ly&quot;&gt;Product Roadmap&lt;/a&gt; to let you in on our new features and enhancements for CE.SDK. This way, we can elaborate our decisions and plans for the product and how you can benefit while allowing you to give us feedback and indicate the importance of particular features.&lt;br&gt;
Now that you know what’s coming down the pike, you can prepare for the release of significant improvements and new features such as &lt;strong&gt;Video Support for Web&lt;/strong&gt; or &lt;strong&gt;Native iOS Support&lt;/strong&gt; well in advance.&lt;/p&gt;
&lt;h3 id=&quot;thanks-for-reading-let-us-know-what-you-think-on-twitter-to-stay-in-the-loop-subscribe-to-our-newsletter&quot;&gt;Thanks for reading! Let us know what you think on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt;! 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;/h3&gt;</content:encoded><dc:creator>Malte</dc:creator><dc:creator>Daniel</dc:creator><media:content url="https://blog.img.ly/2022/08/creative-editor-sdk-design-photo-editor-javascript.png" medium="image"/><category>Release Notes</category><category>CE.SDK</category><category>Roadmap</category><category>Web Application</category><category>Web Development</category><category>Creative Editor</category><category>Design Editor</category><category>Photo Editor</category><category>SDK</category></item><item><title>How To Build a Canva Clone with CE.SDK</title><link>https://img.ly/blog/how-to-build-a-canva-clone-with-ce-sdk/</link><guid isPermaLink="true">https://img.ly/blog/how-to-build-a-canva-clone-with-ce-sdk/</guid><description>Design invitations, greeting cards, flyers, postcards, and business cards with a Canva clone built with CE.SDK in React in minutes. </description><pubDate>Wed, 27 Jul 2022 06:43:59 GMT</pubDate><content:encoded>&lt;p&gt;Canva has popularized image editing, and user expectations of creative capabilities have increased accordingly. If your web or mobile application includes any design functionality - for book covers, t-shirt designs, or social media content - it has to be on par with the design experience offered by prosumer creation tools such as Canva.&lt;/p&gt;
&lt;p&gt;Allowing users to edit images through an easy user interface, define high-quality templates, and give them the ability to share them with the community is a great way to boost engagement, add virality, and potentially new revenue streams.&lt;/p&gt;
&lt;p&gt;You might think this requires a lot of time and effort, but that is not the case. CreativeEditor SDK makes it dead simple to build a Canva-like design editor in minutes. I’ll show you how!&lt;/p&gt;
&lt;p&gt;Follow this step-by-step tutorial and learn how to implement a Canva clone in React with CE.SDK. At the end of this tutorial, you will achieve the following result:&lt;/p&gt;
&lt;p&gt;Try the final result live on &lt;a href=&quot;https://codesandbox.io/embed/how-to-build-a-canva-clone-with-ce-sdk-forked-nvxz3w&quot;&gt;CodeSandbox&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;what-is-canva&quot;&gt;What is Canva?&lt;/h2&gt;
&lt;p&gt;Canva is a free graphic design tool for image editing featuring a drag-and-drop interface for composing different elements on a canvas. You can easily create flyers, invitations, business cards, and more with professionally designed templates. You can think of it as a basic version of Photoshop that anyone can use.&lt;/p&gt;
&lt;p&gt;You can create Canva templates in a web browser or on the official app for iOS or Android, share them and collaborate easily. Templates make Canva a powerful tool - that is also true for CE.SDK, which makes it a perfect harbor to set sail for new horizons and create an excellent editor. Before we learn how to replicate Canva, let us evaluate the SDK in question.&lt;/p&gt;
&lt;h2 id=&quot;what-is-cesdk&quot;&gt;What is CE.SDK?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://img.ly/products/creative-sdk/&quot;&gt;CreativeEditor SDK (CE.SDK)&lt;/a&gt; is a fully customizable, user-friendly design editor tool. You can integrate it easily into your application with just a few lines of code and benefit from the template-based editor in no time.&lt;/p&gt;
&lt;p&gt;The role-specific editing UI focuses heavily on content adaptation. CE.SDK offers two modes: in Creator Mode, you can create a design from scratch or customize existing templates. Once ready, creators can share their templates and decide which elements others can change and to what degree. Then, users can import the template and customize it in Adopter Mode (Default UI).&lt;/p&gt;
&lt;p&gt;Try CE.SDK &lt;a href=&quot;https://img.ly/demos/&quot;&gt;live demos&lt;/a&gt; or &lt;a href=&quot;https://img.ly/docs/cesdk/&quot;&gt;start a free trial&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;designing-and-sharing-a-template-in-creator-mode&quot;&gt;Designing and Sharing a Template in Creator Mode&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://img.ly/docs/cesdk/react/configuration-2c1c3d/&quot;&gt;&lt;code&gt;Creator&lt;/code&gt;&lt;/a&gt; Mode is the most powerful and least restrictive role in CE.SDK. It empowers you to unleash your creativity and create custom templates for every imaginable use case. Add, move, modify, and delete elements as you wish and define constraints. Further elevate your designs with photo editings, such as filters, effects, and background removal.&lt;/p&gt;
&lt;p&gt;The well-rounded text editor also allows you to define variables and programmatically give them a value using the CE.SDK &lt;a href=&quot;https://img.ly/docs/cesdk/react/create-templates/add-dynamic-content/text-variables-7ecb50/&quot;&gt;Variable API&lt;/a&gt;. For example, you may introduce a &lt;code&gt;{{Name}}&lt;/code&gt; Variable and have CESDK import names from a database. This automation is perfect for batch-processing designs, such as greeting cards. Based on this principle, we have built an example &lt;a href=&quot;https://img.ly/blog/how-to-generate-an-nft-art-collection-with-react-using-ce-sdk/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;NFT Art Collection Generator in React with CE.SDK&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the Creator Mode, you also can define &lt;a href=&quot;https://img.ly/docs/cesdk/react/create-templates/add-dynamic-content/placeholders-d9ba8a/&quot;&gt;placeholders&lt;/a&gt;. Turning an element into a placeholder will let you determine if it can be deleted, styled, or duplicated by the adopter.&lt;/p&gt;
&lt;p&gt;This is what the CE.SDK Creator Mode looks like:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;creator-mode-cesdk&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1000px) 1000px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1000&quot; height=&quot;506&quot; src=&quot;https://img.ly/_astro/creator-mode-cesdk_Z1H6JCC.webp&quot; srcset=&quot;/_astro/creator-mode-cesdk_Q0z9g.webp 640w, /_astro/creator-mode-cesdk_1WU4FC.webp 750w, /_astro/creator-mode-cesdk_Z1Ww1nc.webp 828w, /_astro/creator-mode-cesdk_Z1H6JCC.webp 1000w&quot;&gt;&lt;/p&gt;
&lt;p&gt;As you can see, you can use CE.SDK to easily create a wedding invitation card template. This is just an example, and you can find other cool sample templates for greeting cards, flyers, postcards, and business cards on &lt;a href=&quot;https://img.ly/demos/?tags=custom-build-uis&quot;&gt;showcase page&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;what-adopter-mode-offers-you&quot;&gt;What Adopter Mode Offers You&lt;/h3&gt;
&lt;p&gt;You can test the adopter view of the wedding invitation card template shown above &lt;a href=&quot;https://ubique.img.ly/main/apps/dashboard/#/template/Hoc0dPRe5l9BfJaqwCI3&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is what the Adopter Mode looks like:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;adopter-mode&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 999px) 999px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;999&quot; height=&quot;485&quot; src=&quot;https://img.ly/_astro/adopter-mode_Z1qq5LI.webp&quot; srcset=&quot;/_astro/adopter-mode_gAVDi.webp 640w, /_astro/adopter-mode_Z1ThvMI.webp 750w, /_astro/adopter-mode_Z22GG6v.webp 828w, /_astro/adopter-mode_Z1qq5LI.webp 999w&quot;&gt;&lt;/p&gt;
&lt;p&gt;As shown above, CE.SDK’s Adopter Mode enables you to add and modify colors, text, and images based on the constraints enabled by the creator of the template. This mode provides the &lt;a href=&quot;https://img.ly/docs/cesdk/js/configuration-2c1c3d/?ref=img.ly#adopter?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;&lt;code&gt;Adopter&lt;/code&gt;&lt;/a&gt; users with a simple interface, narrowed down to the properties that they are allowed to change. This prevents adopters from accidentally changing or deleting parts of a design that should not be modified.&lt;/p&gt;
&lt;h2 id=&quot;implement-a-canva-clone-with-cesdk&quot;&gt;Implement a Canva Clone with CE.SDK&lt;/h2&gt;
&lt;p&gt;Let’s now learn how to use CE.SDK to build a Canva clone in React.&lt;/p&gt;
&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;This is the list of all the prerequisites for the Canva clone 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 8.0+ and higher&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://react.dev/&quot;&gt;React&lt;/a&gt; &gt;= 18&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/@cesdk/cesdk-js&quot;&gt;&lt;code&gt;@cesdk/cesdk-js&lt;/code&gt;&lt;/a&gt; &gt; 1.6.3&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Add &lt;code&gt;@cesdk/cesdk-js&lt;/code&gt; to your project’s dependencies 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;npm install @cesdk/cesdk-js&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;initialize-a-react-project&quot;&gt;Initialize a React Project&lt;/h3&gt;
&lt;p&gt;You can try out the Canva clone by cloning the &lt;a href=&quot;https://github.com/imgly/canva-clone-react-cesdk&quot;&gt;GitHub repository supporting the article&lt;/a&gt; and running the demo web application with 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/canva-clone-cesdk-imgly&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cd canva-clone-cesdk-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, follow this step-by-step tutorial and learn how to build the Canva clone with CE.SDK by yourself.&lt;/p&gt;
&lt;p&gt;First, you need to initialize a &lt;code&gt;canva-clone-cesdk-imgly&lt;/code&gt; React project. You can do it with &lt;a href=&quot;https://create-react-app.dev/docs/getting-started/&quot;&gt;Create React App&lt;/a&gt; by launching the command below:&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 canva-clone-cesdk-imgly&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, the &lt;code&gt;canva-clone-cesdk-imgly&lt;/code&gt; folder will have 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;canva-clone-cesdk-imgly&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;canva-clone-cesdk-imgly&lt;/code&gt; folder and start a local development server with:&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 canva-clone-cesdk-imgly&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 and make sure you see the default Create React App screen below:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;react&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/react_1REyVa.webp&quot; srcset=&quot;/_astro/react_2lRYbq.webp 640w, /_astro/react_Z21FBPj.webp 750w, /_astro/react_OLsOd.webp 828w, /_astro/react_1REyVa.webp 983w&quot;&gt;&lt;/p&gt;
&lt;p&gt;You are now ready to code!&lt;/p&gt;
&lt;h3 id=&quot;build-the-canva-clone-component-with-cesdk&quot;&gt;Build the Canva Clone Component with CE.SDK&lt;/h3&gt;
&lt;p&gt;If you want your image editing component to provide a Canva-like experience, it must include key features, such as template and custom resource management. Let’s see how to implement them all with CE.SDK.&lt;/p&gt;
&lt;h4 id=&quot;configure-cesdk-to-use-templates&quot;&gt;Configure CE.SDK to use templates&lt;/h4&gt;
&lt;p&gt;Managing templates in CE.SDK is easy. All you have to do is configure the set of predefined templates loaded by CE.SDK on initialization, as explained in the &lt;a href=&quot;https://img.ly/docs/cesdk/react/use-templates/library-b3c704/&quot;&gt;official documentation on adding templates&lt;/a&gt;. You can achieve this as below:&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;useEffect&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;  let&lt;/span&gt;&lt;span&gt; cesdk;&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; config &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;    // initializing CE.SDK with a few templates&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    presets: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      templates: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        postcard_1: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          label: &lt;/span&gt;&lt;span&gt;&apos;Postcard Design&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          scene: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_postcard_1.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          thumbnailURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_postcard_1.png`&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;        postcard_2: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          label: &lt;/span&gt;&lt;span&gt;&apos;Postcard Tropical&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          scene: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_postcard_2.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          thumbnailURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_postcard_2.png`&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;        business_card_1: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          label: &lt;/span&gt;&lt;span&gt;&apos;Business card&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          scene: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_business_card_1.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          thumbnailURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_business_card_1.png`&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;        instagram_photo_1: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          label: &lt;/span&gt;&lt;span&gt;&apos;Instagram photo&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          scene: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_instagram_photo_1.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          thumbnailURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_instagram_photo_1.png`&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;        instagram_story_1: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          label: &lt;/span&gt;&lt;span&gt;&apos;Instagram story&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          scene: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_instagram_story_1.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          thumbnailURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_instagram_story_1.png`&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;        poster_1: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          label: &lt;/span&gt;&lt;span&gt;&apos;Poster&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          scene: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_poster_1.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          thumbnailURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_poster_1.png`&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;        presentation_4: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          label: &lt;/span&gt;&lt;span&gt;&apos;Presentation&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          scene: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_presentation_1.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          thumbnailURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_presentation_1.png`&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;        collage_1: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          label: &lt;/span&gt;&lt;span&gt;&apos;Collage&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          scene: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_collage_1.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          thumbnailURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_collage_1.png`&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;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; (cesdkContainer.current) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    CreativeEditorSDK.&lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt;(cesdkContainer.current, config).&lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;instance&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;      cesdk &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; instance;&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;=&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; (cesdk) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      cesdk.&lt;/span&gt;&lt;span&gt;dispose&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;}, [cesdkContainer]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Thanks to the &lt;code&gt;presets.templates&lt;/code&gt;, you can specify all the CE.SDK templates to be made available to users.&lt;br&gt;
Note that the &lt;code&gt;cesdkContainer&lt;/code&gt; variable stores the &lt;a href=&quot;https://legacy.reactjs.org/docs/refs-and-the-dom.html&quot;&gt;React reference&lt;/a&gt; to the div where to mount CE.SDK.&lt;/p&gt;
&lt;p&gt;This is what the CE.SDK template section will look like:&lt;br&gt;
&lt;img alt=&quot;mount&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1866px) 1866px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1866&quot; height=&quot;893&quot; src=&quot;https://img.ly/_astro/mount_iSSUR.webp&quot; srcset=&quot;/_astro/mount_2jvRJt.webp 640w, /_astro/mount_Zh1IIF.webp 750w, /_astro/mount_aEtiJ.webp 828w, /_astro/mount_Z2irfxL.webp 1080w, /_astro/mount_1YRGvg.webp 1280w, /_astro/mount_CvNzu.webp 1668w, /_astro/mount_iSSUR.webp 1866w&quot;&gt;&lt;/p&gt;
&lt;p&gt;As you can see, all eight templates loaded with &lt;code&gt;presets.templates&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&quot;integrate-external-asset-sources-into-cesdk&quot;&gt;Integrate External Asset Sources Into CE.SDK&lt;/h4&gt;
&lt;p&gt;CE.SDK supports external asset sources, meaning you can give your users a vast library of resources to supercharge their creative workflows. Instead of spending time building a repository of images manually, you can provide your users with image resources retrieved dynamically by connecting to services like Unsplash and Airtable. Learn &lt;a href=&quot;https://img.ly/docs/cesdk/react/import-media/asset-panel/customize-c9a4de/&quot;&gt;how to integrate external asset sources in CE.SDK&lt;/a&gt;, or see a live demo based on the &lt;a href=&quot;https://img.ly/demos/unsplash-image-assets/web/&quot;&gt;Unsplash and Airtable integration&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;add-custom-images-to-cesdk&quot;&gt;Add Custom Images to CE.SDK&lt;/h4&gt;
&lt;p&gt;You can load custom resources and make them available to users with CE.SDK. For example, you would like to add this &lt;a href=&quot;https://www.flaticon.com/free-icon/programming_1208884&quot;&gt;Flaticon&lt;/a&gt; below to the available images in CE.SDK:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;icon&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 512px) 512px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;512&quot; height=&quot;512&quot; src=&quot;https://img.ly/_astro/icon_7W96.webp&quot; srcset=&quot;/_astro/icon_7W96.webp 512w&quot;&gt;&lt;/p&gt;
&lt;p&gt;All you need to do is define a new asset source as explained in the documentation page on &lt;a href=&quot;https://img.ly/docs/cesdk/react/import-media/asset-panel/customize-c9a4de/&quot;&gt;Integrating Custom Resources into CE.SDK&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can achieve this 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;useEffect&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;  // path to the local image to load into CE.SDK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; customImagePath&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; `${&lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;protocol&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; &apos;//&apos;&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;host&lt;/span&gt;&lt;span&gt;}/resources/programming.png`&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; cesdk;&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; config &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;    // loading the business card template as default template&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    initialSceneURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_business_card_1.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // loading the external asset sources&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    assetSources: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      // loading a custom image into CE.SDK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      custom: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        findAssets&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;          return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            assets: [&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;                id: &lt;/span&gt;&lt;span&gt;&apos;custom-image-1&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                type: &lt;/span&gt;&lt;span&gt;&apos;ly.img.image&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                locale: &lt;/span&gt;&lt;span&gt;&apos;en&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                label: &lt;/span&gt;&lt;span&gt;&apos;Programming&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                thumbUri: customImagePath,&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 class=&quot;line&quot;&gt;&lt;span&gt;                  width: &lt;/span&gt;&lt;span&gt;512&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                  height: &lt;/span&gt;&lt;span&gt;512&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;                meta: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                  uri: customImagePath,&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;                context: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                  sourceId: &lt;/span&gt;&lt;span&gt;&apos;custom&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;                credits: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                  name: &lt;/span&gt;&lt;span&gt;&apos;Freepik&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                  url: &lt;/span&gt;&lt;span&gt;&apos;https://www.flaticon.com/free-icon/programming_1208884?related_id=1208782&amp;#x26;origin=search&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&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;            currentPage: &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;            total: &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;            nextPage: &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&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;    // translating the labels associates with the external asset sources&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    i18n: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      en: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &apos;libraries.custom.label&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;Custom&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&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // initializing CE.SDK with a few templates&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    presets: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      templates: {&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;        business_card_1: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          label: &lt;/span&gt;&lt;span&gt;&apos;Business card&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          scene: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_business_card_1.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          thumbnailURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_business_card_1.png`&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;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;  if&lt;/span&gt;&lt;span&gt; (cesdkContainer.current) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    CreativeEditorSDK.&lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt;(cesdkContainer.current, config).&lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;instance&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;      cesdk &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; instance;&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;=&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; (cesdk) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      cesdk.&lt;/span&gt;&lt;span&gt;dispose&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;}, [cesdkContainer]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;customImagePath&lt;/code&gt; variable stores the path to the local image file &lt;code&gt;programming.png&lt;/code&gt; located in the &lt;code&gt;public/resources&lt;/code&gt; folder of the React project. This variable is used in the &lt;code&gt;findAssets()&lt;/code&gt; function. This function is important because it defines the complete asset source. In other words, you only need one function to implement the custom asset retrieval logic. Note that providing images with labels is useful to make them searchable through the CE.SDK UI.&lt;/p&gt;
&lt;p&gt;Now, users will be able to use the image in their template.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;icon-template-ce-sdk-create-canva-yourself&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1000px) 1000px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1000&quot; height=&quot;474&quot; src=&quot;https://img.ly/_astro/icon-template-ce-sdk-create-canva-yourself_1F6dsS.webp&quot; srcset=&quot;/_astro/icon-template-ce-sdk-create-canva-yourself_SgMiL.webp 640w, /_astro/icon-template-ce-sdk-create-canva-yourself_Z1zWsKX.webp 750w, /_astro/icon-template-ce-sdk-create-canva-yourself_wOR2.webp 828w, /_astro/icon-template-ce-sdk-create-canva-yourself_1F6dsS.webp 1000w&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;tie-it-together&quot;&gt;Tie it Together&lt;/h4&gt;
&lt;p&gt;This is what the final &lt;code&gt;CanvaClone&lt;/code&gt; component looks like:&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; &apos;./CanvaClone.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; CreativeEditorSDK &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@cesdk/cesdk-js&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; React, { useEffect, useRef } &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; { findAirtableAssets } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./airtableAssetLibrary&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; { findUnsplashAssets } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./unsplashAssetLibrary&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; CanvaClone&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // initializing Airtable as default external asset library&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  assetLibrary&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &apos;airtable&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;  const&lt;/span&gt;&lt;span&gt; cesdkContainer&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; useRef&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  useEffect&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; externalAssetSources&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&gt;&lt;span&gt;(assetLibrary &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &apos;airtable&apos;&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;        airtable: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          findAssets: findAirtableAssets,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          credits: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            name: &lt;/span&gt;&lt;span&gt;&apos;Airtable&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            url: &lt;/span&gt;&lt;span&gt;&apos;https://airtable.com/shr4x8s9jqaxiJxm5/tblSLR9GBwiVwFS8z?backgroundColor=orange&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&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;(assetLibrary &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &apos;unsplash&apos;&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;        unsplash: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          findAssets: findUnsplashAssets,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          credits: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            name: &lt;/span&gt;&lt;span&gt;&apos;Unsplash&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            url: &lt;/span&gt;&lt;span&gt;&apos;https://unsplash.com/&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;          license: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            name: &lt;/span&gt;&lt;span&gt;&apos;Unsplash license (free)&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            url: &lt;/span&gt;&lt;span&gt;&apos;https://unsplash.com/license&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&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;    // path to the local image to load into CE.SDK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; customImagePath&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; `${&lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;protocol&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; &apos;//&apos;&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;host&lt;/span&gt;&lt;span&gt;}/resources/programming.png`&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; cesdk;&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; config &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;      // loading the business card template as default template&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      initialSceneURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_business_card_1.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      // loading the external asset sources&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      assetSources: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // loading the AirTable or Unsplash asset library&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        ...&lt;/span&gt;&lt;span&gt;externalAssetSources,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // loading a custom image into CE.SDK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        custom: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          findAssets&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;            return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              assets: [&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;                  id: &lt;/span&gt;&lt;span&gt;&apos;custom-image-1&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                  type: &lt;/span&gt;&lt;span&gt;&apos;ly.img.image&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                  locale: &lt;/span&gt;&lt;span&gt;&apos;en&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                  label: &lt;/span&gt;&lt;span&gt;&apos;Programming&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                  thumbUri: customImagePath,&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 class=&quot;line&quot;&gt;&lt;span&gt;                    width: &lt;/span&gt;&lt;span&gt;512&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    height: &lt;/span&gt;&lt;span&gt;512&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;                  meta: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    uri: customImagePath,&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;                  context: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    sourceId: &lt;/span&gt;&lt;span&gt;&apos;custom&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;                  credits: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    name: &lt;/span&gt;&lt;span&gt;&apos;Freepik&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    url: &lt;/span&gt;&lt;span&gt;&apos;https://www.flaticon.com/free-icon/programming_1208884?related_id=1208782&amp;#x26;origin=search&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&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;              currentPage: &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;              total: &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;              nextPage: &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&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;      // translating the labels associates with the external asset sources&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      i18n: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        en: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          &apos;libraries.airtable.label&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;Airtable&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          &apos;libraries.unsplash.label&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;Unsplash&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          &apos;libraries.custom.label&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;Custom&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&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      // initializing CE.SDK with a few templates&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      presets: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        templates: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          postcard_1: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            label: &lt;/span&gt;&lt;span&gt;&apos;Postcard Design&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            scene: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_postcard_1.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            thumbnailURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_postcard_1.png`&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;          postcard_2: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            label: &lt;/span&gt;&lt;span&gt;&apos;Postcard Tropical&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            scene: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_postcard_2.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            thumbnailURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_postcard_2.png`&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;          business_card_1: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            label: &lt;/span&gt;&lt;span&gt;&apos;Business card&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            scene: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_business_card_1.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            thumbnailURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_business_card_1.png`&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;          instagram_photo_1: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            label: &lt;/span&gt;&lt;span&gt;&apos;Instagram photo&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            scene: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_instagram_photo_1.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            thumbnailURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_instagram_photo_1.png`&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;          instagram_story_1: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            label: &lt;/span&gt;&lt;span&gt;&apos;Instagram story&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            scene: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_instagram_story_1.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            thumbnailURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_instagram_story_1.png`&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;          poster_1: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            label: &lt;/span&gt;&lt;span&gt;&apos;Poster&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            scene: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_poster_1.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            thumbnailURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_poster_1.png`&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;          presentation_4: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            label: &lt;/span&gt;&lt;span&gt;&apos;Presentation&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            scene: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_presentation_1.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            thumbnailURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_presentation_1.png`&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;          collage_1: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            label: &lt;/span&gt;&lt;span&gt;&apos;Collage&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            scene: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_collage_1.scene`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            thumbnailURL: &lt;/span&gt;&lt;span&gt;`https://cdn.img.ly/packages/imgly/cesdk-js/latest/assets/templates/cesdk_collage_1.png`&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;span class=&quot;line&quot;&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (cesdkContainer.current) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      CreativeEditorSDK.&lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt;(cesdkContainer.current, config).&lt;/span&gt;&lt;span&gt;then&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;instance&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;          cesdk &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; instance;&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;    return&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; (cesdk) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        cesdk.&lt;/span&gt;&lt;span&gt;dispose&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;  }, [cesdkContainer, assetLibrary]);&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;caseContainer&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; className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wrapper&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; ref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{cesdkContainer} &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;cesdk&quot;&lt;/span&gt;&lt;span&gt;&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;&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; CanvaClone;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;findAirtableAssets()&lt;/code&gt; and &lt;code&gt;findUnsplashAssets()&lt;/code&gt; functions come from the &lt;a href=&quot;https://github.com/imgly/cesdk-web-examples/tree/showcases/showcase-custom-asset-libraries&quot;&gt;GitHub repo&lt;/a&gt; associated with the &lt;a href=&quot;https://img.ly/demos/unsplash-image-assets/web/&quot;&gt;Unsplash and Airtable integration showcase page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These functions allow CE.SDK to dynamically retrieve resources from online services. A search for images in the CE.SDK UI will perform a query on Airtable or Unsplash and provides them to your users. With just a few lines of code, you are making massive resources available to your users.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Canva has rapidly become one of the most popular graphic design tools. It is so prevalent that users expect similar features in your web and mobile applications. Building a Canva clone from scratch would take months, but with CreativeEditor SDK, it only takes minutes.&lt;/p&gt;
&lt;p&gt;In this article, we used CE.SDK to initialize an advanced design editor in React. We adopted its API to configure and customize a design editor to build a Canva clone. This would not be possible without the intuitive UI offered by CE.SDK and its advanced features, such as &lt;a href=&quot;https://img.ly/demos/placeholders/web/&quot;&gt;placeholders&lt;/a&gt; and the possibility to define &lt;a href=&quot;https://img.ly/demos/design-validation/web/&quot;&gt;validation rules&lt;/a&gt; to guide your users’ creation process.&lt;/p&gt;
&lt;p&gt;Here we only scratched the surface of what is possible with CE.SDK – you can use its powerful image processing API to implement many more features and make your Canva clone more and more complex. This is only the beginning!&lt;/p&gt;
&lt;p&gt;See how &lt;a href=&quot;https://img.ly/canva-alternative/&quot;&gt;IMG.LY compares to Canva Connect API&lt;/a&gt; and check out all the use cases of &lt;a href=&quot;https://img.ly/products/creative-sdk/&quot;&gt;CreativeEditor SDK&lt;/a&gt; or &lt;a href=&quot;https://img.ly/forms/contact-sales/&quot;&gt;contact sales&lt;/a&gt; to learn more!&lt;/p&gt;</content:encoded><dc:creator>Antonello</dc:creator><media:content url="https://blog.img.ly/2022/07/create-an-app-like-canva.png" medium="image"/><category>How-To</category><category>Canva</category><category>Design Editor</category><category>Creative Editor</category><category>Web Application</category><category>Web Development</category><category>React</category><category>Tutorial</category><category>Learning</category></item></channel></rss>