<?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>How-To – IMG.LY Blog</title><description>Posts tagged How-To on the IMG.LY blog.</description><link>https://img.ly/blog/tag/how-to/</link><language>en-us</language><image><url>https://img.ly/apple-touch-icon.png</url><title>How-To – IMG.LY Blog</title><link>https://img.ly/blog/tag/how-to/</link></image><atom:link href="https://img.ly/blog/tag/how-to/rss.xml" rel="self" type="application/rss+xml"/><generator>Astro</generator><lastBuildDate>Tue, 09 Jun 2026 09:48:30 GMT</lastBuildDate><ttl>60</ttl><item><title>How to Build a Short Video Generator Using CE.SDK</title><link>https://img.ly/blog/how-to-build-a-short-video-generator-using-ce-sdk-2/</link><guid isPermaLink="true">https://img.ly/blog/how-to-build-a-short-video-generator-using-ce-sdk-2/</guid><description>Build an AI-powered video generator that creates editable, high-quality videos in the browser—no server-side processing needed.</description><pubDate>Tue, 04 Mar 2025 12:57:47 GMT</pubDate><content:encoded>&lt;p&gt;In the following, I’m presenting a simple cookbook for building an AI-based video generator app, as described in &lt;a href=&quot;https://img.ly/blog/how-to-build-short-video-generator-ai-creative-sdk/&quot;&gt;my previous blog post&lt;/a&gt;. We’re using a combination of different APIs to generate text, audio, and images and will compose &amp;#x26; render the final video using the headless &lt;a href=&quot;https://img.ly/products/creative-sdk&quot;&gt;CreativeEditor SDK&lt;/a&gt;. We also call it the Creative Engine.&lt;/p&gt;
&lt;p&gt;This cookbook showcases the powerful capabilities of our client-side Creative Engine. The engine enables real-time video generation directly in the browser, eliminating the need for server-side processing. What sets this approach apart is its ability to produce editable source files that can then be opened with CreativeEditor SDK.&lt;/p&gt;
&lt;p&gt;This approach is giving users complete control over every aspect of your video–from text and images to animations and overall composition. This means your users can refine and perfect your content even after the initial generation.&lt;/p&gt;
&lt;p&gt;Get the &lt;a href=&quot;https://github.com/imgly/ai-video-creator&quot;&gt;complete code on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;scope&quot;&gt;Scope&lt;/h2&gt;
&lt;p&gt;This tutorial focuses on building an app with a simple UX:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Input your keywords/topics&lt;/li&gt;
&lt;li&gt;Choose between landscape or portrait format&lt;/li&gt;
&lt;li&gt;Generate and preview your video&lt;/li&gt;
&lt;li&gt;Edit the video in the CE.SDK video editor&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The app flow we will create:&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/how-to/er-ai2/workflow.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;The post-editing we will get with CE.SDK:&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/how-to/er-ai2/editing-with-ce-sdk.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h2 id=&quot;technical-overview&quot;&gt;Technical Overview&lt;/h2&gt;
&lt;p&gt;The app follows three major steps to generate the video.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A script is generated based on User input, the output is a structured XML file.&lt;/li&gt;
&lt;li&gt;The XML script is parsed to extract text and image information. The extracted data will then be used to generate audio &amp;#x26; image files through third-party APIs&lt;/li&gt;
&lt;li&gt;All assets are loaded into the creative engine. This is where the composition, including animation and effects, takes place. The Creative Engine then exports a video and scene file, which can be edited with the Creative Editor.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;
&lt;p&gt;We’ll use a boilerplate with Next.js, React, Typescript &amp;#x26; Tailwind. Make sure you retrieve all necessary keys:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/&quot;&gt;Anthropic&lt;/a&gt; (LLM)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://elevenlabs.io/&quot;&gt;ElevenLabs&lt;/a&gt; (text to speech)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fal.ai/&quot;&gt;fal.ai&lt;/a&gt; (text to image)&lt;/li&gt;
&lt;li&gt;IMG.LY CE.SDK – Retrieve a &lt;a href=&quot;https://img.ly/forms/free-trial&quot;&gt;free trial key&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Required environment variables&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;NEXT_PUBLIC_ANTHROPIC_API_KEY&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; your_claude_api_key;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;NEXT_PUBLIC_FAL_API_KEY&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; your_fal_ai_key;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;NEXT_PUBLIC_ELEVEN_LABS_KEY&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; your_eleven_labs_key;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;NEXT_PUBLIC_IMG_LY_KEY&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; your_img_ly_key;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;implementation&quot;&gt;Implementation&lt;/h2&gt;
&lt;h3 id=&quot;1-generate-the-script&quot;&gt;1. Generate The Script&lt;/h3&gt;
&lt;p&gt;In this step, we’ll focus on generating the initial prompt and then passing it to the Anthropic API.&lt;/p&gt;
&lt;p&gt;As with many things with LLM, there are many different strategies for structuring the initial prompt. From experience, the best result comes from providing examples of the desired output. We’ve decided to use an XML document; this can be easily parsed later on and is less error-prone compared to a JSON.&lt;/p&gt;
&lt;p&gt;We now define the structure of how information should be saved in the XML.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;video&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;group&lt;/span&gt;&lt;span&gt; part&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;intro&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;element&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;text&lt;/span&gt;&lt;span&gt; voiceId&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;50YSQEDPA2vlOxhCseP4&quot;&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;0.2&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        Did you know these fascinating facts about pyramids?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span&gt;text&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;image&lt;/span&gt;&lt;span&gt;&gt;Ancient Egyptian pyramid at sunset&amp;#x3C;/&lt;/span&gt;&lt;span&gt;image&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;element&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;group&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;group&lt;/span&gt;&lt;span&gt; part&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;content&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;element&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;text&lt;/span&gt;&lt;span&gt; voiceId&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;50YSQEDPA2vlOxhCseP4&quot;&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;0.2&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        The Great Pyramid was the tallest structure for over 3,800 years!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span&gt;text&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;image&lt;/span&gt;&lt;span&gt;&gt;Great Pyramid comparison to modern buildings&amp;#x3C;/&lt;/span&gt;&lt;span&gt;image&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;element&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;group&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;group&lt;/span&gt;&lt;span&gt; part&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;outro&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;element&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;text&lt;/span&gt;&lt;span&gt; voiceId&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;50YSQEDPA2vlOxhCseP4&quot;&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;0.4&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        The pyramids continue to reveal their secrets to this day...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span&gt;text&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;image&lt;/span&gt;&lt;span&gt;&gt;A giant 3D question mark hovering over the pyramids&amp;#x3C;/&lt;/span&gt;&lt;span&gt;image&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;element&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;element&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;text&lt;/span&gt;&lt;span&gt; voiceId&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;50YSQEDPA2vlOxhCseP4&quot;&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;0.4&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        Stay curious - there&apos;s always more to discover!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span&gt;text&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;image&lt;/span&gt;&lt;span&gt;&gt;Pyramids under starry night sky&amp;#x3C;/&lt;/span&gt;&lt;span&gt;image&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;element&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;group&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;video&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this tutorial, we’ll focus on the format trivia only as shown in this example. For later iterations, however, I’m planning to implement different content formats (e.g., trivia, quiz, recipe, etc.). Each of these formats will have its example XML. Therefore, I’m nesting the XML in a simple format object to scale this up easily later.&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;interface&lt;/span&gt;&lt;span&gt; Format&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  example&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&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; formats&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Record&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Format&lt;/span&gt;&lt;span&gt;&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;  trivia: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    name: &lt;/span&gt;&lt;span&gt;&apos;Trivia&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    example: &lt;/span&gt;&lt;span&gt;`&amp;#x3C;video&gt;&amp;#x3C;group&gt;...&amp;#x3C;/group&gt;&amp;#x3C;/video&gt;`&lt;/span&gt;&lt;span&gt; // Add example from above&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;Using this format object with the example, we can now generate the prompt.&lt;/p&gt;
&lt;p&gt;What do we need for this prompt?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Description of the task&lt;/li&gt;
&lt;li&gt;Description of the desired output, incl. an example for the specified format&lt;/li&gt;
&lt;li&gt;Topic as provided by the user&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The topic provided by the user is passed to the function as a string.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&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; createVideoScriptPrompt&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;  topic&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  formatName&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &apos;trivia&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&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; format&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; formats[formatName];&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;format) &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`Format ${&lt;/span&gt;&lt;span&gt;formatName&lt;/span&gt;&lt;span&gt;} not found`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; `&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Format: ${&lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Topic: ${&lt;/span&gt;&lt;span&gt;topic&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;Please write a detailed script for this short video, considering the specified format and topic.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Include an introduction, main content sections, and an outro. Each section should have an image.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Structure the script as an XML Document with clear sections, descriptions for the images.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;The image description should be written as a prompt. This prompt will be used to generate an image.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Put the description between the image tags. The video shouldn&apos;t be longer than 30 seconds.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Example format:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;example&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;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;2-generate-all-assets&quot;&gt;2. Generate All Assets&lt;/h3&gt;
&lt;p&gt;In the second step, we’ll parse through the LLM response, which should be the XML. We’ll create a simple parsing function to extract all text information that should be sent to text-to-speech and text-to-image AIs.&lt;/p&gt;
&lt;p&gt;Please note that all these steps can be easily streamlined by using AI-assisted coding. Just provide the example XML as input and your desired output.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;API Calls&lt;/strong&gt;&lt;br&gt;
When finding text &amp;#x26; image tags in the XML, we’ll call API functions for text-to-speech and text-to-image. For this example, I’m using ElevenLabs &amp;#x26; fal APIs. You will find all API calls in the api.ts.&lt;/p&gt;
&lt;p&gt;Since the LLM generated a script that includes image prompts, make sure to pass them to the API.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; async&lt;/span&gt;&lt;span&gt; function&lt;/span&gt;&lt;span&gt; generateImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;prompt&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Promise&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Generating image for prompt:&apos;&lt;/span&gt;&lt;span&gt;, prompt);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; fal.&lt;/span&gt;&lt;span&gt;subscribe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;fal-ai/flux/dev&apos;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      input: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        prompt: prompt,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        size: &lt;/span&gt;&lt;span&gt;&apos;portrait_16_9&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;    const&lt;/span&gt;&lt;span&gt; typedResult&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; result &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;images&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; string&lt;/span&gt;&lt;span&gt; }[] };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Image generation successful. URL:&apos;&lt;/span&gt;&lt;span&gt;, typedResult.images[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;].url);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; typedResult.images[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;].url;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (error) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Error generating image:&apos;&lt;/span&gt;&lt;span&gt;, error);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&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;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Timestamps&lt;/strong&gt;&lt;br&gt;
Last but not least, we need to come up with timestamps. What’s the duration of each segment? This is critical information for composing the video. Luckily, this is quite easy: Each scene is as long as the generated audio for each segment. This duration for the audio segments can be calculated: Most TTS like ElevenLabs provide timestamps along the audio file. These are typically character-based timestamps, so we first have to calculate the timestamps for each word and then the duration for the entire text section.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ready For The Next Steps&lt;/strong&gt;&lt;br&gt;
All Asset URLs that are generated will be saved in a &lt;code&gt;VideoBlock&lt;/code&gt; object for convenience. The duration of the VideoBlock is the duration of the audio, as calculated above.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; VideoBlock&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  text&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  imageUrl&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&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;  audioUrl&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&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;  startTime&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; number&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  duration&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; number&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  wordTimestamps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Array&lt;/span&gt;&lt;span&gt;&amp;#x3C;{ &lt;/span&gt;&lt;span&gt;word&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; number&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;duration&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; number&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;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;3-generate-the-video&quot;&gt;3. Generate The Video&lt;/h3&gt;
&lt;p&gt;We have everything together now: The completed XML with timestamps, duration, and all assets. It’s now time to generate the video using the creative engine.&lt;/p&gt;
&lt;p&gt;Let’s first add an empty container in our HTML that will be referenced for initiating the creative engine.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&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;  /* Add container for Creative Engine */&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; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;cesdk_container&quot;&lt;/span&gt;&lt;span&gt; className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;invisible mt-8 rounded-lg bg-gray-100&quot;&lt;/span&gt;&lt;span&gt; /&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now initialize the engine. Use &lt;a href=&quot;https://img.ly/docs/cesdk/js/get-started/overview-e18f40/&quot;&gt;this code&lt;/a&gt; snippet from our documentation.&lt;/p&gt;
&lt;p&gt;We’ll then set up a function that creates a simple composition using the provided VideoBlocks. The engine requires you to first create a scene, append a page to the scene, and then create tracks within the page. The tracks are basically the layers in the timeline. I recommend setting one track as a background track using the following snippet:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Set video track as a background track by connecting the page duration to the video track&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.&lt;/span&gt;&lt;span&gt;setAlwaysOnBottom&lt;/span&gt;&lt;span&gt;(videotrack, &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;engine.block.&lt;/span&gt;&lt;span&gt;setPageDurationSource&lt;/span&gt;&lt;span&gt;(page, videotrack);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Creative Engine provides powerful API calls to style &amp;#x26; manipulate blocks in many ways. Here is an example of how we can animate the images with a slow zoom effect:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; imageZoomAnimation&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;createAnimation&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;crop_zoom&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.&lt;/span&gt;&lt;span&gt;setInAnimation&lt;/span&gt;&lt;span&gt;(image, imageZoomAnimation);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.&lt;/span&gt;&lt;span&gt;setDuration&lt;/span&gt;&lt;span&gt;(imageZoomAnimation, block.duration);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.&lt;/span&gt;&lt;span&gt;setBool&lt;/span&gt;&lt;span&gt;(imageZoomAnimation, &lt;/span&gt;&lt;span&gt;&apos;animation/crop_zoom/fade&apos;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Export The Video &amp;#x26; Scene&lt;/strong&gt;&lt;br&gt;
Exporting the video is easy. Just pass the page to the export function. In our example, we’re also saving the scene file so we can edit the video later.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Export video&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; progressCallback&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;  renderedFrames&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; number&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  encodedFrames&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; number&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  totalFrames&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; number&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&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;  console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`Progress: ${&lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;round&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;encodedFrames&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; totalFrames&lt;/span&gt;&lt;span&gt;) &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&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; blob&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;exportVideo&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  page,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;video/mp4&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  progressCallback,&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;// Save scene to string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; sceneData&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; engine.scene.&lt;/span&gt;&lt;span&gt;saveToString&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;// Create scene blob&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; sceneBlob&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Blob&lt;/span&gt;&lt;span&gt;([sceneData], {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  type: &lt;/span&gt;&lt;span&gt;&apos;text/plain&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;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;4-add-a-video-editor&quot;&gt;4. Add A Video Editor&lt;/h3&gt;
&lt;p&gt;The last step is to add the video editor for post editing and pass the scene file. With CE.SDK, this effort is reduced to adding a few lines of code. In the &lt;code&gt;init&lt;/code&gt; function, we’re configuring the editor and adding callbacks for the export:&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;const&lt;/span&gt;&lt;span&gt; initEditor&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;        const&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;          license: &lt;/span&gt;&lt;span&gt;&apos;A-O53TWXK5bfyconUx7e53S5YU7DzjuGpMAH5vvKjLd0zBa6IhsoF7zChy1uCVbj&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          userId: &lt;/span&gt;&lt;span&gt;&apos;guides-user&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          theme: &lt;/span&gt;&lt;span&gt;&apos;dark&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          baseURL: &lt;/span&gt;&lt;span&gt;&apos;&amp;#x3C;https://cdn.img.ly/packages/imgly/cesdk-js/1.44.0/assets&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          role: &lt;/span&gt;&lt;span&gt;&apos;Creator&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          ui: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            elements: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              view: &lt;/span&gt;&lt;span&gt;&apos;default&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              panels: {&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;              navigation: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                position: &lt;/span&gt;&lt;span&gt;&apos;top&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                action: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                  save: &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;                  load: &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;                  close: &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;                  download: &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;                  export: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              dock: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                iconSize: &lt;/span&gt;&lt;span&gt;&apos;normal&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// &apos;large&apos; or &apos;normal&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                hideLabels: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; // false or true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          callbacks: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            onUpload: &lt;/span&gt;&lt;span&gt;&apos;local&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            onSave&lt;/span&gt;&lt;span&gt;: (&lt;/span&gt;&lt;span&gt;scene&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&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; element&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;createElement&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;a&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; base64Data&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; btoa&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;unescape&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;encodeURIComponent&lt;/span&gt;&lt;span&gt;(scene)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              element.&lt;/span&gt;&lt;span&gt;setAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                &apos;href&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                `data:application/octet-stream;base64,${&lt;/span&gt;&lt;span&gt;base64Data&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;              element.&lt;/span&gt;&lt;span&gt;setAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                &apos;download&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                `video-${&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; Date&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;toISOString&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;}.scene`&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;              element.style.display &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;none&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              document.body.&lt;/span&gt;&lt;span&gt;appendChild&lt;/span&gt;&lt;span&gt;(element)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              element.&lt;/span&gt;&lt;span&gt;click&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              document.body.&lt;/span&gt;&lt;span&gt;removeChild&lt;/span&gt;&lt;span&gt;(element)&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;            onClose&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;              onClose&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;            onLoad: &lt;/span&gt;&lt;span&gt;&apos;upload&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            onDownload: &lt;/span&gt;&lt;span&gt;&apos;download&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            onExport: &lt;/span&gt;&lt;span&gt;&apos;download&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;By following this cookbook, you can streamline the process of AI-generated video creation, making it fast and efficient. This method is especially useful for content creators, educators, and marketers looking to automate video production while maintaining creative control.&lt;br&gt;
Next, try experimenting with video styles, refining AI scripts, or exploring advanced editing.&lt;br&gt;
Feel free to &lt;a href=&quot;https://github.com/imgly/ai-video-creator&quot;&gt;GitHub&lt;/a&gt; repo and share your creations &lt;a href=&quot;https://x.com/imgly&quot;&gt;with us on X&lt;/a&gt;. Happy creating!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3,000+ creative professionals gain early access to new features and updates—don’t miss out, and&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i?ref=img.ly&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>Eray</dc:creator><media:content url="https://blog.img.ly/2025/03/AI-video-generator-tutorial-1.jpg" medium="image"/><category>AI</category><category>CE.SDK</category><category>How-To</category></item><item><title>How I Built a Short Video Generator with AI &amp; CE.SDK in One Day</title><link>https://img.ly/blog/how-to-build-short-video-generator-ai-creative-sdk/</link><guid isPermaLink="true">https://img.ly/blog/how-to-build-short-video-generator-ai-creative-sdk/</guid><description>Can AI really accelerate product development? I built a short video generator in one day using AI and CE.SDK to find out.</description><pubDate>Thu, 09 Jan 2025 11:27:13 GMT</pubDate><content:encoded>&lt;p&gt;Here’s the crux of product development in the age of LLMs: how much can AI truly accelerate the development process?&lt;/p&gt;
&lt;p&gt;We have seen videos of solo developers building small apps entirely with AI with just a few prompts. But how does it scale to more complex development projects? As LLMs rapidly evolve, their scope and impact will only increase.&lt;/p&gt;
&lt;p&gt;That’s why I regularly challenge myself to build a small project with the help of AI. I’m a prime candidate to test the AI productivity boost: a jack-of-all-trades (and a master of none) with a background in both design and engineering, yet no hands-on experience in the past five years. My latest challenge? Build a web-based short video generator within one day.&lt;/p&gt;
&lt;p&gt;In this post, I’ll share the most intriguing takeaways from tackling this project.&lt;/p&gt;
&lt;h2 id=&quot;why-a-short-video-generator&quot;&gt;Why a Short Video Generator?&lt;/h2&gt;
&lt;p&gt;Why focus on this idea? It’s simple: to ride the wave of a new trend. A format called “&lt;strong&gt;faceless&lt;/strong&gt;” short videos is gaining traction among creators on platforms like YouTube and TikTok.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/embed/DfQ3fhqfKVc?feature=oembed&quot;&gt;https://www.youtube.com/embed/DfQ3fhqfKVc?feature=oembed&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What’s fascinating about these videos is their automation: an LLM generates a script, which is then transformed into speech, images, and text assets using various AI services. These assets are automatically assembled into a cohesive video.&lt;/p&gt;
&lt;p&gt;The general concept is compelling: It’s still generative content, but mixed with classic video composition techniques. This approach offers greater accuracy, consistency, and control over pure generative AI.&lt;/p&gt;
&lt;p&gt;The potential to automate video production at this scale is exciting. Add its relatively low complexity and high production value, and it became the perfect topic for my challenge.&lt;/p&gt;
&lt;h2 id=&quot;enter-cesdk&quot;&gt;Enter CE.SDK&lt;/h2&gt;
&lt;p&gt;Another reason I chose this challenge was its compatibility with CE.SDK, our design and video editor library. CE.SDK offers a robust editing toolkit that integrates into any product with just a few lines of code. Its features, like headless mode, are ideal for automating workflows like video generation.&lt;/p&gt;
&lt;p&gt;Most faceless video services use React-based video generation and achieve fair results. However, using CE.SDK instead of a react-based library could potentially boost the overall experience with three critical improvements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Editable Outputs:&lt;/strong&gt; This is huge. Full automation often needs human adjustments for fine-tuning. CE.SDK enables automated video generation while allowing manual refinement of the results.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enhanced Visual Quality:&lt;/strong&gt; CE.SDK has its own rendering pipeline, allowing for more nuanced visual effects and animations. When you’re competing against others in this space, it can make a huge difference if you’re able to produce higher fidelity in the visual output.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visual Design Workflow:&lt;/strong&gt; Create design components or even entire templates visually, and then use them via code. This authoring workflow can be extremely helpful in creating rich, interesting designs for the generated videos.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-ground-rules&quot;&gt;The Ground Rules&lt;/h2&gt;
&lt;p&gt;To keep the challenge focused, I set strict rules:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Time Limit:&lt;/strong&gt; Spend no more than 12 hours on the challenge.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No Manual Coding:&lt;/strong&gt; Avoid writing any code yourself—everything should be built through conversations with AI.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trust the AI:&lt;/strong&gt; Do not read or analyze code generated by the AI. Rely entirely on its decisions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skip External Research:&lt;/strong&gt; Do not read or explore the APIs you intend to use. Instead, provide links to the AI and let it determine how to use them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compare AI Performance:&lt;/strong&gt; Alternate Claude Sonnet 3.5 and ChatGPT o1 for code generation to evaluate which performs better.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-tools--workflow&quot;&gt;The Tools &amp;#x26; Workflow&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Code Editor:&lt;/strong&gt; &lt;a href=&quot;https://www.cursor.com/&quot;&gt;&lt;strong&gt;Cursor&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;
Built on VSCode’s foundation, Cursor stood out as the only editor offering both an integrated chat interface and the ability to switch between different LLMs. However, with GitHub’s recent significant updates to Copilot, I’ll switch to VSCode with Copilot for future challenges.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UI Prototyping:&lt;/strong&gt; &lt;a href=&quot;https://madewithclaude.com/&quot;&gt;&lt;strong&gt;Claude Artifacts&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;
Rather than building the entire project in my code editor, I chose to prototype the UI directly through Claude’s web interface. The benefits were immense:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Instant results:&lt;/strong&gt; To create an artifact, Claude streamlines development by automatically writing and compiling code while leveraging essential UI libraries and components. This automation eliminates setup time and technical overhead, allowing me to focus purely on design iterations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Instants Variations:&lt;/strong&gt; Claude enables rapid prototyping through parallel conversations. When a design direction didn’t quite work, I could simply start a fresh conversation with modified requirements and evaluate a new prototype. This approach helped me develop three viable concepts quickly - a pace that would have been impossible in a traditional code editor.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quality of execution:&lt;/strong&gt; Claude transforms rough concepts into polished, intuitive interfaces. Its suggestions often surpassed my initial ideas, offering sophisticated solutions I hadn’t considered.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep it clean:&lt;/strong&gt; By prototyping outside the code editor, I kept the main project’s codebase clean and focused. This separation prevented the accumulation of experimental code and maintained the clarity of our primary development environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/how-to/er-ai/0108.mp4&quot; controls loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Quickly prototype your interface with Claude Artifacts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;APIs&lt;/strong&gt;&lt;br&gt;
Key APIs used in the project included:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Script Generation:&lt;/strong&gt; Claude Sonnet 3.5 vs various ChatGPT models.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Image/Video Assets:&lt;/strong&gt; &lt;a href=&quot;https://Fal.ai&quot;&gt;Fal.ai&lt;/a&gt; Flux models.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Speech Synthesis:&lt;/strong&gt; &lt;a href=&quot;https://elevenlabs.io/&quot;&gt;ElevenLabs&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;building-the-app-divide-and-conquer&quot;&gt;Building the App: Divide and Conquer&lt;/h2&gt;
&lt;p&gt;After having prototyped the UI, I started to chat with the LLM inside the code editor so that it can code the app. To work with the AI efficiently, I followed a divide-and-conquer approach. Rather than simply asking it to “build me a video app,” I broke down the problem into manageable steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Generate a video script&lt;/strong&gt;&lt;br&gt;
Create an AI prompt that includes user input and examples of the desired output format. Pass this prompt to the LLM API.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parse the script to generate assets (speech, images, text)&lt;/strong&gt;&lt;br&gt;
Parse the LLM’s response to extract image prompts and speech paragraphs. Send these to their respective APIs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compose the final video&lt;/strong&gt;&lt;br&gt;
Load all the generated assets into a predefined template to generate the finished video through the CE.SDK library.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After completing these steps, I was finally able to generate my first fully automated videos! With a few more tweaks and additions, I had an MVP ready within twelve hours.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/how-to/er-ai/shortyroll.mp4&quot; controls loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;The final result: A Short Video Generator&lt;/p&gt;
&lt;p&gt;There are still some missing features, partly because I spent a significant amount of time refining the prompt to generate the video script. I also had to bend the rules occasionally—sometimes the LLM would hit a wall, and I had to read or write small snippets of code.&lt;/p&gt;
&lt;h2 id=&quot;key-takeaways&quot;&gt;Key Takeaways&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Engineering Knowledge Is Essential&lt;/strong&gt;&lt;br&gt;
You should have some engineering background to achieve the AI productivity boost in development.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AI doesn’t solve everything for you. You are still the architect. You provide a lot of input and guidance. AI often needs to be pointed to the right strategy. Foundational knowledge of computer science is hugely advantageous for working with AI effectively.&lt;/li&gt;
&lt;li&gt;As mentioned, I had to read and write a few lines of code myself. Without coding experience, I would have probably not been able to progress, as the LLM was not able to.&lt;/li&gt;
&lt;li&gt;The getting started experience is nowhere close to novice-friendly. How do you get started with a new project in a code editor that actually requires you to do the setup manually? My workaround was to create an empty project, and then ask the LLM to instruct me to use a boilerplate for react. Again, this is engineering knowledge, any novice would have hit a wall already at this point.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Claude Outperformed ChatGPT&lt;/strong&gt;&lt;br&gt;
Claude was a clear winner in the side-by-side comparison, because of three reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Claude Artifacts was a game changer for UI prototyping.&lt;/li&gt;
&lt;li&gt;It was generally better at writing and understanding code. Difficult to quantify, but in some cases Claude fixed the mess ChatGPT left in the code&lt;/li&gt;
&lt;li&gt;Claude can process URLs, which makes working with APIs much smoother.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Who would have thought new LLMs would catch up to OpenAI so quickly after they released the first version of ChatGPT?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Complexity Slows AI&lt;/strong&gt;&lt;br&gt;
The more code in my project, the slower the overall progress. LLMs struggled with the growing complexity. Their context windows filled more quickly, and their responses became increasingly unreliable. At some point, it becomes extremely difficult to make architectural changes, especially if this affects multiple parts of the app. When trying to fix errors, you’ll often find yourself in a whack-a-mole game. While the AI would resolve one issue, it would inadvertently introduce new problems elsewhere, creating an endless loop of fixes and regressions.&lt;/p&gt;
&lt;p&gt;Ultimately, the time invested in this challenge was well worth it. While LLMs can’t build products end to end on their own, they can significantly streamline product development when paired with the right human collaboration. The real question is whether development teams are ready to adapt their habits and explore new workflows to boost productivity.&lt;/p&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;This challenge has inspired me to refine and expand on this project. Future iterations will focus on harnessing CE.SDK’s unique features to push the boundaries of automated video generation.&lt;/p&gt;
&lt;p&gt;Stay tuned for part two of this series—there’s much more to explore!&lt;/p&gt;
&lt;p&gt;UPDATE: Read &lt;a href=&quot;https://img.ly/blog/how-to-build-a-short-video-generator-using-ce-sdk-2/&quot;&gt;part two&lt;/a&gt; - a cookbook how to build your own short video creator!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Over 3,000 creative professionals gain early access to our new features, insights and updates—don’t miss out, and&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i?ref=img.ly&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>Eray</dc:creator><media:content url="https://blog.img.ly/2025/01/build-ai-video-generator-1.jpg" medium="image"/><category>How-To</category><category>AI</category><category>CE.SDK</category><category>Artificial Intelligence</category><category>Video Editor</category></item><item><title>A Modern Vue.js Video Editor: Setup Guide</title><link>https://img.ly/blog/a-modern-vue-js-video-editor/</link><guid isPermaLink="true">https://img.ly/blog/a-modern-vue-js-video-editor/</guid><description>Learn how to integrate IMG.LY&apos;s video editor for Vue.js into your web app and how to best leverage its capabilities.</description><pubDate>Wed, 08 Jan 2025 11:24:43 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Discover how to integrate&lt;/em&gt; &lt;a href=&quot;https://img.ly/docs/cesdk/vue/starterkits/video-editor-e1nlor/&quot;&gt;&lt;em&gt;IMG.LY’s video editor&lt;/em&gt;&lt;/a&gt; &lt;em&gt;into your Vue.js application and unlock its full potential.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Platforms like Instagram, YouTube, and TikTok have revolutionized how we engage with content, setting new standards for consuming video media. With the growing popularity of short-form videos, this trend shows no signs of slowing down.&lt;/p&gt;
&lt;p&gt;Users now not only love to watch videos but also expect intuitive video creation and editing features from modern web applications. Here is why adding a video editor to your Vue.js web application can significantly boost user engagement and satisfaction.&lt;/p&gt;
&lt;p&gt;In this guide, you will discover how to integrate a video editor into a Vue.js project using CreativeEditor SDK (also referred to as &lt;em&gt;CE.SDK&lt;/em&gt;). We will also discuss when this functionality is most beneficial and how to customize the editor for your specific needs.&lt;/p&gt;
&lt;p&gt;For a quick setup, take a look at the &lt;a href=&quot;https://github.com/imgly/cesdk-web-examples/tree/main/integrate-with-vue&quot;&gt;GitHub Vue.js video editor integration&lt;/a&gt; repository to kick off your project.&lt;/p&gt;
&lt;p&gt;Let’s dive in!&lt;/p&gt;
&lt;h2 id=&quot;why-you-should-add-a-video-editor-to-your-vuejs-app&quot;&gt;Why You Should Add a Video Editor to Your Vue.js App?&lt;/h2&gt;
&lt;p&gt;The digital landscape is constantly evolving, but one truth has remained steady: video content continues to dominate.&lt;/p&gt;
&lt;p&gt;Statistics about videos are clear. According to &lt;a href=&quot;https://datareportal.com/reports/digital-2022-instagram-headlines&quot;&gt;Datareportal&lt;/a&gt;, over one-third of all Instagram reels are videos. Similarly, YouTube Shorts garners &lt;a href=&quot;https://blog.youtube/inside-youtube/shorts-revenue-sharing-update/&quot;&gt;over 70 billion daily views&lt;/a&gt;, and TikTok boasts more than &lt;a href=&quot;https://www.statista.com/statistics/272014/global-social-networks-ranked-by-number-of-users/&quot;&gt;1.5 billion monthly active users worldwide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The reasons behind these staggering numbers are two:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Videos are incredibly engaging for audiences.&lt;/li&gt;
&lt;li&gt;Social media platforms have made it remarkably easy to create professional-looking content.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Users now expect to be able to add filters, overlays, audio, music, and text with just a few clicks. As a result, not only do we anticipate video editing features in web applications, but we also demand that these features be simple to use.&lt;/p&gt;
&lt;p&gt;By integrating an intuitive video editor, your Vue.js web app can capitalize on that trend. The goal is to provide a feature that billions of users are already familiar with—and likely expect from your application.&lt;/p&gt;
&lt;p&gt;The payoff? Lowering the barriers to user-generated content, increasing engagement, and driving product distribution!&lt;/p&gt;
&lt;h2 id=&quot;how-to-integrate-a-video-editor-in-vuejs&quot;&gt;How to Integrate a Video Editor in Vue.js&lt;/h2&gt;
&lt;p&gt;In this section, you will learn how to set up a Vue.js video editor using CreativeEditor SDK.&lt;/p&gt;
&lt;h3 id=&quot;requirements&quot;&gt;Requirements&lt;/h3&gt;
&lt;p&gt;Before starting, ensure you have the latest LTS version of Node.js installed on your machine. Otherwise, download and install it from the &lt;a href=&quot;https://nodejs.org/en/download&quot;&gt;official Node.js website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you do not already have a Vue.js project, create a new &lt;a href=&quot;https://vite.dev/&quot;&gt;Vite&lt;/a&gt;-based Vue project by running the following command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; create&lt;/span&gt;&lt;span&gt; vue@latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will install and run &lt;a href=&quot;https://github.com/vuejs/create-vue&quot;&gt;create-vue&lt;/a&gt;, the official Vue project scaffolding tool. During the process, you will be prompted to select optional features like TypeScript and testing support. Below is an example of how you can respond to the prompts:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;√&lt;/span&gt;&lt;span&gt; Project&lt;/span&gt;&lt;span&gt; name:&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;span&gt; my-vue-video-editor-app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;√&lt;/span&gt;&lt;span&gt; Add&lt;/span&gt;&lt;span&gt; TypeScript?&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;span&gt; No&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; Yes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;√&lt;/span&gt;&lt;span&gt; Add&lt;/span&gt;&lt;span&gt; JSX&lt;/span&gt;&lt;span&gt; Support?&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;span&gt; No&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; Yes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;√&lt;/span&gt;&lt;span&gt; Add&lt;/span&gt;&lt;span&gt; Vue&lt;/span&gt;&lt;span&gt; Router&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt; Single&lt;/span&gt;&lt;span&gt; Page&lt;/span&gt;&lt;span&gt; Application&lt;/span&gt;&lt;span&gt; development?&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;span&gt; No&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; Yes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;√&lt;/span&gt;&lt;span&gt; Add&lt;/span&gt;&lt;span&gt; Pinia&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt; state&lt;/span&gt;&lt;span&gt; management?&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;span&gt; No&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; Yes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;√&lt;/span&gt;&lt;span&gt; Add&lt;/span&gt;&lt;span&gt; Vitest&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt; Unit&lt;/span&gt;&lt;span&gt; Testing?&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;span&gt; No&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; Yes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;√&lt;/span&gt;&lt;span&gt; Add&lt;/span&gt;&lt;span&gt; an&lt;/span&gt;&lt;span&gt; End-to-End&lt;/span&gt;&lt;span&gt; Testing&lt;/span&gt;&lt;span&gt; Solution?&lt;/span&gt;&lt;span&gt; »&lt;/span&gt;&lt;span&gt; No&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;√&lt;/span&gt;&lt;span&gt; Add&lt;/span&gt;&lt;span&gt; ESLint&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt; code&lt;/span&gt;&lt;span&gt; quality?&lt;/span&gt;&lt;span&gt; »&lt;/span&gt;&lt;span&gt; Yes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;√&lt;/span&gt;&lt;span&gt; Add&lt;/span&gt;&lt;span&gt; Prettier&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt; code&lt;/span&gt;&lt;span&gt; formatting?&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;span&gt; No&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; Yes&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are unsure about an option, simply select “No” by pressing Enter.&lt;/p&gt;
&lt;p&gt;After the project is set up, navigate to the project folder and install CreativeEditor SDK via &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; package with this command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; @cesdk/cesdk-js&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your package.json file will now include the SDK as a dependency:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;dependencies&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;@cesdk/cesdk-js&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^1.41.1&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;vue&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^3.5.13&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;vue-router&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^4.4.5&quot;&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;Finally, open your project in a JavaScript IDE, and you are ready to start coding!&lt;/p&gt;
&lt;h3 id=&quot;create-the-video-editor-component&quot;&gt;Create the Video Editor Component&lt;/h3&gt;
&lt;p&gt;In the &lt;code&gt;src/components&lt;/code&gt; folder, create a new file named &lt;code&gt;VideoEditor.vue&lt;/code&gt;. This file will serve as your Vue.js single-file component for the CE.SDK-based video editor.&lt;/p&gt;
&lt;p&gt;In the  like this:&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; 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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, create your video editor component using the following lines of code:&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;!--&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;components&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;VideoEditor.vue &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 class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;cesdk_container&quot;&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;height: 100vh; width: 100vw&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;template&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 class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import CreativeEditorSDK from &apos;@cesdk/cesdk-js&apos;&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 default {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  props: { config: Object },&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;  _cesdk: &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;  mounted: &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; mounted&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    CreativeEditorSDK.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;#cesdk_container&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.config).&lt;/span&gt;&lt;span&gt;then&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;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;      this&lt;/span&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;      // customize the editor behavior...&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;  methods: {},&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;  watch: {},&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;  beforeUnmount: &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; beforeDestroy&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // clean up the CE.SDK instance&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;this&lt;/span&gt;&lt;span&gt;._cesdk) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      this&lt;/span&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;      this&lt;/span&gt;&lt;span&gt;._cesdk &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; null&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;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The component defined above embeds the &lt;code&gt;CreativeEditorSDK&lt;/code&gt; JavaScript element into an HTML &lt;code&gt;&amp;#x3C;div&gt;&lt;/code&gt; container.&lt;/p&gt;
&lt;h3 id=&quot;use-the-component&quot;&gt;Use the Component&lt;/h3&gt;
&lt;p&gt;You can now import the &lt;code&gt;VideoEditor&lt;/code&gt; component in the 
&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; VideoEditor &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./components/VideoEditor.vue&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, include it in the &lt;template&gt; section as shown in this snippet:
&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;VideoEditor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  :config=&quot;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    license:&lt;/span&gt;&lt;span&gt; &apos;&amp;#x3C;YOUR_LICENSE&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    userId:&lt;/span&gt;&lt;span&gt; &apos;&amp;#x3C;YOUR_USER_ID&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace &lt;code&gt;&amp;#x3C;YOUR_LICENSE&gt;&lt;/code&gt; with the license key provided by CreativeEditor SDK and &lt;code&gt;&amp;#x3C;YOUR_USER_ID&gt;&lt;/code&gt; with your user ID to optionally track monthly active users (MAUs) across different devices.&lt;/p&gt;
&lt;p&gt;This setup will launch the video editor with a video preset, enabling users to trim, cut, apply filters, add text overlays, include music, and perform other enhancements to their videos.&lt;/p&gt;
&lt;h2 id=&quot;use-cases-for-the-vuejs-video-editor&quot;&gt;Use Cases for the Vue.js Video Editor&lt;/h2&gt;
&lt;p&gt;Now that you have seen how to build a Vue.js video editor component with a basic CreativeEditor SDK setup, it is time to explore some practical use cases and scenarios.&lt;/p&gt;
&lt;h3 id=&quot;automated-marketing&quot;&gt;Automated Marketing&lt;/h3&gt;
&lt;p&gt;CreativeEditor SDK equips marketers with powerful tools to produce branded videos for multiple campaigns at scale. Key features for this task include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API Integration&lt;/strong&gt;: Integrate the SDK’s API to automate the creation of videos, reducing manual effort and supporting large-scale video production. This opens the door to automated workflows for marketing campaigns, ad creation, and A/B testing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pre-Designed, Editable Templates&lt;/strong&gt;: Define placeholders, lock or unlock specific design elements, and guide what other users can edit and how. Thanks to this template-based process, it is possible to produce professional assets by leveraging editable &lt;a href=&quot;https://img.ly/showcases/cesdk/video-ui/web&quot;&gt;video templates&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Template Import from Photoshop:&lt;/strong&gt; Configure the SDK to bulk import .PSD files from Adobe Photoshop, converting them into the CE.SDK scene archive format for streamlined design integration and editing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manage Branded Assets&lt;/strong&gt;: Build &lt;a href=&quot;https://img.ly/docs/cesdk/vue/import-media/asset-panel/customize-c9a4de/&quot;&gt;custom content libraries&lt;/a&gt; to manage brand assets, including logos, vector graphics, stock photos, and templates.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These capabilities make CE.SDK a reliable technology for scalable, efficient, and brand-consistent video marketing automation.&lt;/p&gt;
&lt;h3 id=&quot;informative-videos-for-e-learning&quot;&gt;Informative Videos for E-Learning&lt;/h3&gt;
&lt;p&gt;CreativeEditor SDK gives educators, teachers, and coaches the tools they need to create impactful and information-rich videos. The entire content creation and editing process requires no advanced technical skills. In detail, users can record, edit, and organize videos across desktop, mobile, or tablet devices.&lt;/p&gt;
&lt;p&gt;Beyond simplifying video production, the SDK is based on reusable templates, so that instructors can quickly adapt materials for various topics while maintaining consistency. Additionally, &lt;a href=&quot;https://img.ly/docs/cesdk/vue/user-interface/ui-extensions-d194d1/&quot;&gt;interactivity plugins&lt;/a&gt;—featuring quizzes, polls, and other learner-focused elements—enhances engagement, making lessons more dynamic and personalized.&lt;/p&gt;
&lt;h3 id=&quot;unique-videos-for-lead-outreach&quot;&gt;Unique Videos for Lead Outreach&lt;/h3&gt;
&lt;p&gt;Cold emails often feel dull and impersonal, frequently ending up in the spam or trash folder. In contrast, personalized video messages are revolutionizing lead outreach.&lt;/p&gt;
&lt;p&gt;CreativeEditor SDK empowers sales, marketing, and business development teams to deliver engaging videos that outshine standard email pitches, offering features such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Customized Video Messages for Leads&lt;/strong&gt;: Craft personalized videos for specific clients or prospects, adding a genuine human touch to your messages. Thanks to &lt;a href=&quot;https://img.ly/docs/cesdk/vue/create-templates/add-dynamic-content/text-variables-7ecb50/&quot;&gt;configurable text variables&lt;/a&gt;, you can customize video content at scale to leave a lasting impression compared to traditional cold emails.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Seamless Integration for Scalable Outreach&lt;/strong&gt;: Produce professional and customized video messages at scale, making it easy for sales teams to personalize outreach without sacrificing efficiency.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Translation Capabilities for Global Reach&lt;/strong&gt;: Leverage the SDK’s built-in translation and internationalization features to communicate with audiences worldwide. Adapt your messages to the local customs to convey a truly personalized experience. Test this feature &lt;a href=&quot;https://img.ly/showcases/cesdk/language/web&quot;&gt;in our translation demo&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Take your lead outreach to the next level with these innovative video solutions.&lt;/p&gt;
&lt;h3 id=&quot;social-media-video-content-generation&quot;&gt;Social Media Video Content Generation&lt;/h3&gt;
&lt;p&gt;CE.SDK offers all the tools users need to create captivating videos. It makes it easy to produce shareable content for platforms like Instagram, TikTok, YouTube, LinkedIn, and more.&lt;/p&gt;
&lt;p&gt;Users can take advantage of reusable templates to rapidly bring their ideas to life, without having to start from scratch. They can choose from a variety of pre-designed options, which you can explore in &lt;a href=&quot;https://img.ly/showcases/cesdk/video-ui/web&quot;&gt;our video editor demo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The support for &lt;a href=&quot;https://img.ly/docs/cesdk/vue/animation/overview-6a2ef2/&quot;&gt;configurable animations&lt;/a&gt; increase engagement and improve the visual appealing. In the SDK, users can also set the mood with music or add narration. Once complete, videos can finally be seamlessly exported in the ideal format and aspect ratio for social media platforms.&lt;/p&gt;
&lt;h3 id=&quot;digital-asset-management&quot;&gt;Digital Asset Management&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://business.adobe.com/blog/basics/digital-asset-management&quot;&gt;Digital Asset Management&lt;/a&gt;, commonly referred to as DAM, is the process of organizing, storing, and managing digital assets—such as videos. Given the large volume of assets companies produce and the way employees and users interact with them, DAM has become a key business process for many organizations.&lt;/p&gt;
&lt;p&gt;CreativeEditor SDK supports DAM by offering an embeddable Vue.js video editor UI component, allowing for centralized video organization, production, repurposing, and editing. This component exposes features like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Asset Libraries&lt;/strong&gt;: CE.SDK can load assets from local files or even integrate third-party libraries via API. Users can search and browse local assets as well as remote images from platforms like Pexels, Unsplash, and Getty within the editor. You can explore our &lt;a href=&quot;https://img.ly/showcases/cesdk?tags=assets&quot;&gt;asset library demo&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Permissions for Asset Control&lt;/strong&gt;: Although the SDK does not include role-based permissions out of the box, it connects with backend technologies to manage user access and permissions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extendability for Custom Permissions&lt;/strong&gt;: Developers can extend the SDK to create custom hooks that check permissions and modify the editor’s behavior, ensuring the right users have access to and can edit assets.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These features make CreativeEditor SDK a valuable tool for businesses managing video assets while maintaining brand consistency and workflow efficiency.&lt;/p&gt;
&lt;h2 id=&quot;customizing-your-creativeeditor-sdk-instance&quot;&gt;Customizing your CreativeEditor SDK Instance&lt;/h2&gt;
&lt;p&gt;CreativeEditor SDK does not provide a static, pre-configured Vue.js video editor experience. Instead, it supports a large set of customization options to tailor the editor to your specific needs and brand requirements.&lt;/p&gt;
&lt;p&gt;Key customization features include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Build Custom UIs&lt;/strong&gt;: Create fully personalized user interfaces and adapt the editor to your specific use case. This includes repositioning toolbar elements, changing icons, or renaming tools to provide a unique editing experience.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feature Activation&lt;/strong&gt;: Enable or disable features based on default settings, giving you control over what is available to users.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Internationalization (i18n)&lt;/strong&gt;: Tailor the video editor to different languages and regions. CE.SDK &lt;a href=&quot;https://img.ly/docs/cesdk/vue/user-interface/localization-508e20/&quot;&gt;fully supports i18n&lt;/a&gt;, enabling you to overwrite and extend all text strings in any language.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Media Libraries&lt;/strong&gt;: Define custom, sortable, resource-rich media libraries. CE.SDK supports integration with third-party libraries via API for easy access to external media assets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Single Page Mode&lt;/strong&gt;: Configure the editor to display only one active page at a time for a streamlined experience.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Theming&lt;/strong&gt;: Adjust the editor’s theme to align with your app’s design. Choose from built-in themes, generate new ones using the &lt;a href=&quot;https://img.ly/docs/cesdk/vue/user-interface/appearance/theming-4b0938/&quot;&gt;theme generator&lt;/a&gt;, or manually create a custom theme. Test theming in our &lt;a href=&quot;https://img.ly/showcases/cesdk/theming/web&quot;&gt;demo page&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See all of these customizations in action in our &lt;a href=&quot;https://img.ly/showcases/cesdk?tags=customization&quot;&gt;customization demo&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Featuring a Vue.js video editor in your web application can greatly improve the user experience, making it a valuable addition to boosting product success, user reviews, and retention. This is true whether you are building a sales tool, an e-learning platform, or a web app with social media integration.&lt;/p&gt;
&lt;p&gt;With CreativeEditor SDK, you can integrate a fully customizable video editing experience into your Vue.js application in minutes.&lt;/p&gt;
&lt;p&gt;By following the steps outlined in this blog post, you saw how to bring professional-level video editing features to your web users. Explore CE.SDK’s video capabilities and &lt;a href=&quot;https://img.ly/docs/cesdk/vue/prebuilt-solutions/video-editor-9e533a/&quot;&gt;dive into the docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Stay tuned for more updates, and please &lt;a href=&quot;https://img.ly/forms/contact-sales?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=plugins1&quot;&gt;reach out&lt;/a&gt; if you have any questions. Thank you for reading.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Over 3,000 creative professionals gain early access to our new features, demos, and updates—don’t miss out, and&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i?ref=img.ly&quot;&gt;&lt;strong&gt;subscribe&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;to our newsletter.&lt;/strong&gt;&lt;/p&gt;&lt;/template&gt;&lt;/p&gt;</content:encoded><dc:creator>Antonello</dc:creator><media:content url="https://blog.img.ly/2025/01/video-editor-vue_js.jpg" medium="image"/><category>Vue.js</category><category>Video Editor</category><category>How-To</category></item><item><title>A Modern React Video Editor: Integration Guide</title><link>https://img.ly/blog/a-modern-react-video-editor/</link><guid isPermaLink="true">https://img.ly/blog/a-modern-react-video-editor/</guid><description>Learn how to integrate IMG.LY&apos;s video editor for React Native into your app and how to best leverage its capabilities.</description><pubDate>Wed, 08 Jan 2025 09:31:11 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Learn how to integrate&lt;/em&gt; &lt;a href=&quot;https://img.ly/docs/cesdk/react/starterkits/video-editor-e1nlor/&quot;&gt;&lt;em&gt;IMG.LY’s video editor for React&lt;/em&gt;&lt;/a&gt; &lt;em&gt;into your web app and make the most of all its features.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Video content has seen steadily growing demand, especially with the rising popularity of short-form videos. It is no surprise that platforms like YouTube have heavily invested in short-form video content, as video remains a dominant medium for users to create and consume content.&lt;/p&gt;
&lt;p&gt;Platforms like Instagram, TikTok, and YouTube have changed user habits forever. Here is why integrating video creation and editing directly within your web application can dramatically enhance user satisfaction.&lt;/p&gt;
&lt;p&gt;In this guide, you will see how to integrate a video editor into a React app using CreativeEditor SDK (&lt;em&gt;CE.SDK&lt;/em&gt;, for short). We will discuss when it is useful and explain how to customize the editor for your specific use cases.&lt;/p&gt;
&lt;p&gt;For a quick start, check out the &lt;a href=&quot;https://github.com/imgly/cesdk-web-examples/tree/main/integrate-with-react&quot;&gt;GitHub repository&lt;/a&gt; featuring the code for integrating a React video editor.&lt;/p&gt;
&lt;p&gt;Let’s dive in!&lt;/p&gt;
&lt;h2 id=&quot;why-integrate-a-video-editor-in-your-react-web-app&quot;&gt;Why Integrate a Video Editor in Your React Web App?&lt;/h2&gt;
&lt;p&gt;The digital landscape is ever-evolving, but one truth has remained constant for several years: video content reigns supreme.&lt;/p&gt;
&lt;p&gt;The data speaks for itself. Platforms like &lt;a href=&quot;https://newsroom.tiktok.com/en-us/1-billion-people-on-tiktok&quot;&gt;TikTok boast over 1.04 billion monthly active users worldwide&lt;/a&gt;. Similarly, one of most recent &lt;a href=&quot;https://abc.xyz/2024-q1-earnings-call/&quot;&gt;Google’s earnings call&lt;/a&gt; revealed that YouTube Shorts generated approximately 70 billion daily views in just one quarter—a significant leap from 30 billion daily views just two years ago.&lt;/p&gt;
&lt;p&gt;These platforms have set the gold standard by empowering users to create professional-looking videos effortlessly—adding filters, overlays, audio, and text with just a few clicks. This simplicity has made video editing an essential feature across various industries, from social media apps to marketing tools.&lt;/p&gt;
&lt;p&gt;Then, the shift toward shorter, more engaging videos has transformed how users create content and how we consume it. Audiences now demand bite-sized, engaging clips that are easy to produce and share.&lt;/p&gt;
&lt;p&gt;By integrating an easy-to-use video editor, your React web app can tap into this trend. The idea is to offer a feature that billions of users are familiar with—and may already expect from your application.&lt;/p&gt;
&lt;p&gt;The result? Lowering the barrier for user-generated content, boosting engagement, and driving distribution!&lt;/p&gt;
&lt;p&gt;React’s flexibility and component-based architecture make it the perfect match for IMG.LY’s &lt;a href=&quot;https://img.ly/products/creative-sdk&quot;&gt;CreativeEditor SDK&lt;/a&gt;, which guarantees a seamless, high-performance, and feature-rich experience for design, video, and photo editing—all directly in the browser.&lt;/p&gt;
&lt;h2 id=&quot;getting-started-adding-a-video-editor-in-react&quot;&gt;Getting Started: Adding a Video Editor in React&lt;/h2&gt;
&lt;p&gt;Follow this step-by-step tutorial section and learn how to set up a React video editor in your web app using CreativeEditor SDK. For a different approach, see our guide on &lt;a href=&quot;https://img.ly/blog/how-to-build-a-video-editor-with-wasm-in-react/&quot;&gt;how to build a video editor in React with Wasm&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Integrate CE.SDK in React with the following instructions!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Requirements&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Before getting started, make sure you have the latest LTS version of Node.js installed on your machine. Otherwise, &lt;a href=&quot;https://nodejs.org/en/download&quot;&gt;download it from the official website&lt;/a&gt; and install it.&lt;/p&gt;
&lt;p&gt;Next, if you do not already have a React project set up, create a new one using &lt;a href=&quot;https://vite.dev/guide/&quot;&gt;Vite&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; create&lt;/span&gt;&lt;span&gt; vite@latest&lt;/span&gt;&lt;span&gt; my-react-video-editor-app&lt;/span&gt;&lt;span&gt; –&lt;/span&gt;&lt;span&gt; --template&lt;/span&gt;&lt;span&gt; react&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install CE.SDK via the &lt;a href=&quot;https://www.npmjs.com/package/@cesdk/cesdk-js&quot;&gt;@cesdk/cesdk-js&lt;/a&gt; package with this command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; @cesdk/cesdk-js&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your &lt;code&gt;package.json&lt;/code&gt; file will now include it as a dependency:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;dependencies&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;@cesdk/cesdk-js&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^1.41.1&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;react&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^18.3.1&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;react-dom&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^18.3.1&quot;&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;Load your project in a JavaScript IDE, as you are ready to start coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create the Video Editor Component&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In the src folder, create a &lt;code&gt;VideoEditor.jsx&lt;/code&gt; file. This will contain your CE.SDK-based React video editor component.&lt;/p&gt;
&lt;p&gt;Import CreativeEditorSDK from &lt;code&gt;@cesdk/cesdk-js&lt;/code&gt; 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;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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, initialize your video editor React component with this logic:&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;// src/VideoEditor.jsx&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; 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; { useEffect, 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;// your CE.SDK license and user configs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&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;  license: &lt;/span&gt;&lt;span&gt;&apos;&amp;#x3C;YOUR_LICENSE&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  userId: &lt;/span&gt;&lt;span&gt;&apos;&amp;#x3C;YOUR_USER_ID&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; default&lt;/span&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; cesdk_container&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; [&lt;/span&gt;&lt;span&gt;cesdk&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setCesdk&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;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;    // if the CE.SDK container has already been initialized&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;cesdk_container.current) {&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;    let&lt;/span&gt;&lt;span&gt; cleanedUp &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;    // initialize a CE.SDK instance&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    let&lt;/span&gt;&lt;span&gt; instance;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    CreativeEditorSDK.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(cesdk_container.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;      async&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;        instance &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;        if&lt;/span&gt;&lt;span&gt; (cleanedUp) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          instance.&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;          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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // customize the editor behavior...&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;        setCesdk&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;    // clean up the CE.SDK instance&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; cleanup&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;      cleanedUp &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;      instance?.&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;      setCesdk&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&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; cleanup;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }, [cesdk_container]);&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      ref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{cesdk_container}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{{ width: &lt;/span&gt;&lt;span&gt;&apos;100vw&apos;&lt;/span&gt;&lt;span&gt;, height: &lt;/span&gt;&lt;span&gt;&apos;100vh&apos;&lt;/span&gt;&lt;span&gt; }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;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;Replace &lt;code&gt;&amp;#x3C;YOUR_LICENSE&gt;&lt;/code&gt; with the license key you obtain from CreativeEditor SDK and &lt;code&gt;&amp;#x3C;YOUR_USER_ID&gt;&lt;/code&gt; with your user ID to optionally track monthly active users (MAUs) across different devices.&lt;/p&gt;
&lt;p&gt;The component defined above loads the &lt;a href=&quot;https://img.ly/docs/cesdk/js/get-started/overview-e18f40/&quot;&gt;CreativeEditorSDK&lt;/a&gt; JavaScript element into an HTML &lt;/p&gt;&lt;div&gt; element.&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use the Component&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can now import the VideoEditor 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;import&lt;/span&gt;&lt;span&gt; VideoEditor &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./VideoEditor&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, use it as below:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;#x3C;VideoEditor /&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This will open the video editor with the video preset, allowing users to trim, cut, apply filters, add text overlays, include music, and more to enhance their videos.&lt;/p&gt;
&lt;p&gt;Have a look at our &lt;a href=&quot;https://img.ly/showcases/cesdk/video-ui/web?ref=img.ly&quot;&gt;interactive demo&lt;/a&gt; to see what the resultant editor should look like.&lt;/p&gt;
&lt;h2 id=&quot;react-video-editor-use-cases&quot;&gt;React Video Editor Use Cases&lt;/h2&gt;
&lt;p&gt;Now that you know how to integrate a React video editor into your web app, you are ready to explore some key use cases. See how the specific features of CreativeEditor SDK can support different scenarios.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Social Media Publishing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;CreativeEditor SDK equips users with the tools they need to create engaging video content. It makes it easier to produce shareable videos for popular social media platforms like Instagram, TikTok, and LinkedIn with features like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reusable Templates&lt;/strong&gt;: Start with video templates downloaded from others or create your own templates and share them with the community. By selecting from a variety of ready-to-use templates for different use cases, users can avoid the blank canvas syndrome and bring their ideas to life quickly. You can explore a range of different templates in our &lt;a href=&quot;https://img.ly/showcases/cesdk/video-ui/web?template=month-in-review&amp;#x26;ref=img.ly&quot;&gt;video editor demo&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Music and Audio Library&lt;/strong&gt;: Users can easily add audio files to their videos, setting the tone through music or narration and creating a captivating atmosphere for their content.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Animations&lt;/strong&gt;: The SDK comes with a wide set of &lt;a href=&quot;https://img.ly/docs/cesdk/react/animation/overview-6a2ef2/&quot;&gt;configurable animations&lt;/a&gt; to improve the appearance of design elements within video scenes, significantly boosting engagement.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once a video has been created in your React application, CE.SDK lets users export it in the appropriate formats and aspect ratios for the target social media platforms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Digital Asset Management&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;DAM, short for &lt;a href=&quot;https://www.ibm.com/think/topics/digital-asset-management&quot;&gt;Digital Asset Management&lt;/a&gt;, refers to the process of organizing, storing, and managing any digital asset—including videos. Over time, DAM has become essential for businesses to maintain efficiency, streamline workflows, and ensure brand consistency across teams.&lt;/p&gt;
&lt;p&gt;CreativeEditor SDK supports DAM by giving your team the ability to organize, edit, and repurpose video content within a centralized, embeddable system. Within the React video editor component, users can easily adapt and edit their assets, ensuring brand consistency while automatically propagating changes across multiple assets.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://img.ly/showcases/cesdk/video-ui/web&quot;&gt;Video templates&lt;/a&gt; support placeholders and permissions to guide users’ designs, guaranteeing that all assets stay on-brand. While CE.SDK does not directly support role-based permissions, it can be integrated with any backend technology or framework to control user access.&lt;/p&gt;
&lt;p&gt;Keep in mind that the SDK is also extendable. That way, developers can create custom hooks that check permissions and customize the editor’s behavior accordingly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Marketing Automation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;CreativeEditor SDK provides &lt;a href=&quot;https://img.ly/industries/marketing-tech&quot;&gt;marketing tech&lt;/a&gt; to efficiently craft branded, campaign-specific videos at scale.&lt;/p&gt;
&lt;p&gt;Features like pre-designed, editable, and customizable designs help save time in video production and management. Changes to these templates can be automatically propagated, also eliminating the need for manual updates.&lt;/p&gt;
&lt;p&gt;The SDK also supports advanced scenarios like A/B testing, so that your marketing teams can campaign reach by comparing different solutions to see how they perform. Plus, the ability to generate customized videos with &lt;a href=&quot;https://img.ly/docs/cesdk/react/create-templates/add-dynamic-content/text-variables-7ecb50/&quot;&gt;configurable text variables&lt;/a&gt; makes personalizing content easier than ever.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Personalized Videos for Sales Outreach&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We all know how boring and annoying cold emails can be. Marketers and salespeople understand this too, which is why personalized video messages have become the new frontier for pitching.&lt;/p&gt;
&lt;p&gt;Thanks to CreativeEditor SDK, sales teams can easily produce customized videos tailored to specific clients or leads. These videos are far more engaging than standard emails because they feel like a real conversation.&lt;/p&gt;
&lt;p&gt;Also, the translation capabilities offered by the SDK allow sales teams to reach multiple audiences worldwide, breaking down language barriers and personalizing the message for a broader range of targets. Test this feature in our &lt;a href=&quot;https://img.ly/showcases/cesdk/language/web&quot;&gt;translation and internationalization demo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;E-Learning&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;CreativeEditor SDK provides educators with the tools they need to create informative videos. Teachers and coaches can quickly produce professional-quality content without requiring extensive technical skills.&lt;/p&gt;
&lt;p&gt;The SDK enhances the learning experience on your React-based e-learning platform through features like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reusability&lt;/strong&gt;: Build a library of templates that can be easily adapted and integrated into existing materials.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easy video production&lt;/strong&gt;: Record, edit, and arrange videos directly within the editor, accessible on desktop, mobile, or tablet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plugins for learner interactivity&lt;/strong&gt;: Boost engagement by &lt;a href=&quot;https://img.ly/docs/cesdk/react/user-interface/ui-extensions-d194d1/&quot;&gt;integrating plugins&lt;/a&gt; that add quizzes, polls, and other interactive components within the videos.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;customizing-your-creativeeditor-sdk-instance&quot;&gt;Customizing your CreativeEditor SDK Instance&lt;/h2&gt;
&lt;p&gt;CreativeEditor SDK does not offer a static, pre-configured React video editor experience. Quite the opposite, it presents several customization features to tailor the editor to your specific needs and brand requirements.&lt;/p&gt;
&lt;p&gt;The key customization options are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Build Custom UIs&lt;/strong&gt;: Define fully custom user interfaces adapted to your specific use case.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Theming&lt;/strong&gt;: Adjust the editor’s theme to match your app’s design. Choose from built-in themes, generate new ones using the &lt;a href=&quot;https://img.ly/docs/cesdk/react/user-interface/appearance/theming-4b0938/&quot;&gt;theme generator&lt;/a&gt;, or manually create a custom theme. Test theming in our &lt;a href=&quot;https://img.ly/showcases/cesdk/theming/web&quot;&gt;dedicated demo&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Media Libraries&lt;/strong&gt;: Set up customizable, sortable media libraries. CE.SDK supports integration with third-party libraries accessible via API, such as &lt;a href=&quot;https://img.ly/showcases/cesdk/unsplash-image-assets/web&quot;&gt;Unsplash&lt;/a&gt;, allowing you to easily access and use external media assets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Toolbar Customization&lt;/strong&gt;: Reposition toolbar elements, change icons, or rename tools to suit your needs, providing a unique editing experience.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Internationalization&lt;/strong&gt;: Adapt the video editor component to different languages and regions. CE.SDK offers &lt;a href=&quot;https://img.ly/docs/cesdk/react/user-interface/localization-508e20/&quot;&gt;full i18n support&lt;/a&gt;, enabling you to overwrite and extend all text strings in any language.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Integrating a React video editor into your web app can significantly enhance the user experience and serve as a valuable feature to boost retention, engagement, and potential product distribution. This holds true whether you are building a social media platform, a tool for influencers, a marketing SaaS, or an e-learning system.&lt;/p&gt;
&lt;p&gt;With CreativeEditor SDK, you can quickly add a fully customizable video editing experience to your React application in just minutes.&lt;/p&gt;
&lt;p&gt;By following the steps outlined in this blog post, you can bring professional-level video editing features to your web users. Explore CE.SDK’s video capabilities and &lt;a href=&quot;https://img.ly/docs/cesdk/react/prebuilt-solutions/video-editor-9e533a/&quot;&gt;dive into the docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Stay tuned for more updates, and please &lt;a href=&quot;https://img.ly/forms/contact-sales?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=plugins1&quot;&gt;reach out&lt;/a&gt; if you have any questions. Thank you for reading.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Over 3,000 creative professionals gain early access to our new features, demos, and updates—don’t miss out, and&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i?ref=img.ly&quot;&gt;&lt;strong&gt;subscribe&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;to our newsletter.&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;</content:encoded><dc:creator>Antonello</dc:creator><media:content url="https://blog.img.ly/2025/01/video-editor-react.jpg" medium="image"/><category>React</category><category>How-To</category><category>Video Editor</category></item><item><title>A Modern Angular Video Editor: Setup Guide</title><link>https://img.ly/blog/a-modern-angular-video-editor/</link><guid isPermaLink="true">https://img.ly/blog/a-modern-angular-video-editor/</guid><description>Learn how to integrate IMG.LY&apos;s video editor for Angular into your web app and how to best leverage its capabilities.</description><pubDate>Wed, 08 Jan 2025 08:19:25 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Learn how to integrate&lt;/em&gt; &lt;a href=&quot;https://img.ly/docs/cesdk/angular/starterkits/video-editor-e1nlor/&quot;&gt;&lt;em&gt;IMG.LY’s video editor&lt;/em&gt;&lt;/a&gt; &lt;em&gt;for Angular into your web application and get the most out of it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;TikTok, Instagram, Facebook, WhatsApp Messenger, YouTube, and X are among the most popular social media platforms. They consistently rank in the top 10 most visited websites and downloaded mobile apps globally. Have you ever asked what is the common thread between them? Video content!&lt;/p&gt;
&lt;p&gt;These platforms have revolutionized how we consume video content. Videos are now shorter, often include subtitles for silent viewing, and are designed to be engaging, professionally produced, and artfully crafted.The challenge lies in meeting user expectations, as modern web applications are now expected to offer intuitive video creation and editing tools. After all, Instagram and TikTok have set the bar quite high when it comes to seamless video production.&lt;/p&gt;
&lt;p&gt;This guide will walk you through implementing a video editor in your Angular web app using CreativeEditor SDK (&lt;em&gt;CE.SDK&lt;/em&gt;). We will also take a look at some scenarios where this video editing is useful and explain how to customize the editor to meet your unique requirements.To jumpstart your project, check out the &lt;a href=&quot;https://github.com/imgly/cesdk-web-examples/tree/main/integrate-with-angular&quot;&gt;GitHub repository for Angular video editor integration&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let’s dive in!&lt;/p&gt;
&lt;h2 id=&quot;why-add-a-video-editor-to-an-angular-application&quot;&gt;Why Add a Video Editor to an Angular Application?&lt;/h2&gt;
&lt;p&gt;Over the past few months, AI has been revolutionizing the tech industry. It all started with AI-generated text and images, but what is next on the horizon? AI-generated videos!&lt;/p&gt;
&lt;p&gt;The appeal is clear—numerous studies show that video is the most engaging medium, resonating deeply with us as humans. This is no surprise, given that &lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_most-visited_websites&quot;&gt;YouTube and Instagram rank among the top five most visited websites&lt;/a&gt;, and &lt;a href=&quot;https://www.statista.com/statistics/1285960/top-downloaded-mobile-apps-worldwide/&quot;&gt;TikTok is the most downloaded app worldwide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The problem is that users now demand more than just video content. They expect engaging, professionally edited videos. On top of that, platforms like Instagram and TikTok have raised the bar for intuitive video production and editing. &lt;/p&gt;
&lt;p&gt;As a result, we all expect to be able to add filters, overlays, audio, music, and text to videos with just a few clicks. Meeting these expectations is essential for any application offering video generation features.&lt;/p&gt;
&lt;p&gt;Adding an easy-to-use video editor into your Angular project gives users a powerful tool that they are likely to desire and are already familiar with. That will surely boosts user engagement and help you accelerate the distribution of your online product or service!&lt;/p&gt;
&lt;h2 id=&quot;how-to-set-up-a-video-editor-in-angular&quot;&gt;How to Set Up a Video Editor in Angular&lt;/h2&gt;
&lt;p&gt;In this section, you will learn how to integrate an Angular video editor into your wev application through CreativeEditor SDK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Requirements&lt;/strong&gt;&lt;br&gt;
Before you begin, make sure you have the latest LTS version of Node.js installed on your machine. If that is not your case, download and install it from the &lt;a href=&quot;https://nodejs.org/en/download&quot;&gt;official Node.js website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next, open your terminal and run the following command to install the &lt;a href=&quot;https://angular.dev/tools/cli&quot;&gt;Angular CLI&lt;/a&gt; globally:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; -g&lt;/span&gt;&lt;span&gt; @angular/cli&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will make it easy to create and manage Angular projects.&lt;/p&gt;
&lt;p&gt;Once the installation is complete, if you don’t have an Angular project set up, initialize a new Angular project by running:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ng&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; my-angular-video-editor-app&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will be prompted with a few questions:&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;Would you like to share pseudonymous usage data about this project with the Angular Team at Google under Google&apos;s Privacy Policy at https://policies.google.com/privacy. For more details and how to change this setting, see https://angular.dev/cli/analytics.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;No&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Global setting: disabled&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Local setting: No local workspace configuration file.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Effective status: disabled&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;✔ Which stylesheet format would you like to use? CSS [ https://developer.mozilla.org/docs/Web/CSS ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;✔ Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? No&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are unsure, press Enter to accept the default answer.&lt;/p&gt;
&lt;p&gt;After the project is created, navigate to the project folder and install the CreativeEditor SDK (&lt;a href=&quot;https://www.npmjs.com/package/@cesdk/cesdk-js&quot;&gt;@cesdk/cesdk-js&lt;/a&gt;) npm package by running:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm install @cesdk/cesdk-js&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Your &lt;strong&gt;package.json&lt;/strong&gt; file will now include the SDK as a dependency:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;dependencies&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;@angular/animations&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^19.0.0&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;@angular/common&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^19.0.0&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;@angular/compiler&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^19.0.0&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;@angular/core&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^19.0.0&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;@angular/forms&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^19.0.0&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;@angular/platform-browser&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^19.0.0&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;@angular/platform-browser-dynamic&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^19.0.0&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;@angular/router&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^19.0.0&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;@cesdk/cesdk-js&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^1.41.1&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;rxjs&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;~7.8.0&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;tslib&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^2.3.0&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;zone.js&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;~0.15.0&quot;&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;Open your project in a JavaScript IDE, as you are ready to start coding!&lt;/p&gt;
&lt;h3 id=&quot;create-a-video-editor-component&quot;&gt;Create a Video Editor Component&lt;/h3&gt;
&lt;p&gt;First, use the Angular CLI to generate the scaffold for your video editor component:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ng&lt;/span&gt;&lt;span&gt; generate&lt;/span&gt;&lt;span&gt; component&lt;/span&gt;&lt;span&gt; video-editor&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open the &lt;code&gt;video-editor.component.ts&lt;/code&gt; file inside the &lt;code&gt;src/app/video-editor&lt;/code&gt; folder and initialize your CE.SDK-based Angular video editor 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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Component, ElementRef, ViewChild } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@angular/core&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, { Configuration } &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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Component&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  selector: &lt;/span&gt;&lt;span&gt;&apos;app-video-editor&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  imports: [],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  templateUrl: &lt;/span&gt;&lt;span&gt;&apos;./video-editor.component.html&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  styleUrl: &lt;/span&gt;&lt;span&gt;&apos;./video-editor.component.css&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; VideoEditorComponent&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ViewChild&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;cesdk_container&apos;&lt;/span&gt;&lt;span&gt;) containerRef: ElementRef = {} &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; ElementRef&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;  ngAfterViewInit&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; void&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; config&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Configuration&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;      license: &lt;/span&gt;&lt;span&gt;&apos;&amp;#x3C;YOUR_LICENSE&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      userId: &lt;/span&gt;&lt;span&gt;&apos;&amp;#x3C;YOUR_USER_ID&gt;&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;    CreativeEditorSDK.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.containerRef.nativeElement, 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;      async&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; any&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;        // customize the editor behavior...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        await&lt;/span&gt;&lt;span&gt; instance.&lt;/span&gt;&lt;span&gt;createDesignScene&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace &lt;code&gt;&amp;#x3C;YOUR_LICENSE&gt;&lt;/code&gt; with the license key provided by CreativeEditor SDK. Optionally, also set &lt;code&gt;&amp;#x3C;YOUR_USER_ID&gt;&lt;/code&gt; to your user ID to start collecting monthly active user (MAU) data.Then, define video-editor.component.html as below:&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;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; #cesdk_container&lt;/span&gt;&lt;span&gt; [style.height.vh]=&quot;&apos;100&apos;&quot;&lt;/span&gt;&lt;span&gt; [style.width.vw]=&quot;&apos;100&apos;&quot;&gt;&amp;#x3C;/div&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way, VideoEditorComponent will be mounted on the above &lt;/p&gt;&lt;div&gt; element.&lt;p&gt;&lt;/p&gt;
&lt;h3 id=&quot;use-the-component&quot;&gt;Use the Component&lt;/h3&gt;
&lt;p&gt;You can now import the component and register it in your &lt;code&gt;app.components.ts&lt;/code&gt; file with these lines of code:&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; { Component } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@angular/core&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; { RouterOutlet } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@angular/router&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; { VideoEditorComponent } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./video-editor/video-editor.component&apos;&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;// import the video editor component&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Component&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  selector: &lt;/span&gt;&lt;span&gt;&apos;app-root&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  imports: [RouterOutlet, VideoEditorComponent], &lt;/span&gt;&lt;span&gt;// register the video editor component&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  templateUrl: &lt;/span&gt;&lt;span&gt;&apos;./app.component.html&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  styleUrl: &lt;/span&gt;&lt;span&gt;&apos;./app.component.css&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;export&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; AppComponent&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  title&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &apos;my-angular-video-editor-app&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, you can use it in app.component.html 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;&amp;#x3C;app-video-editor&gt;&amp;#x3C;/app-video-editor&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This setup will open the video editor with a predefined video template, allowing users to trim, cut, apply filters, add text overlays, insert music, and make other video enhancements.&lt;/p&gt;
&lt;p&gt;Have a look at our &lt;a href=&quot;https://img.ly/showcases/cesdk/video-ui/web&quot;&gt;interactive demo&lt;/a&gt; to see what the resultant editor should look like.&lt;/p&gt;
&lt;h2 id=&quot;use-cases-the-your-angular-video-editor&quot;&gt;Use Cases the Your Angular Video Editor&lt;/h2&gt;
&lt;p&gt;After learning how to create an Angular video editor component with a basic CreativeEditor SDK integration, it is time to explore some practical use cases in real-world scenarios.&lt;/p&gt;
&lt;h3 id=&quot;video-messages-for-marketing-outreach&quot;&gt;Video Messages for Marketing Outreach&lt;/h3&gt;
&lt;p&gt;Cold emails often come across as generic and impersonal, often ending up in spam or being ignored. In contrast, personalized &lt;a href=&quot;https://www.linkedin.com/pulse/harnessing-power-video-messages-sales-outreach-oliveira-phd--fqrle/&quot;&gt;video messages are transforming marketing outreach&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;CreativeEditor SDK enables marketers to create compelling videos that stand out from standard email approaches with capabilities like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Personalized Video Messages for Leads&lt;/strong&gt;: Create tailored videos for individual clients or prospects, adding a personal touch to your marketing outreach efforts and make a stronger impact compared to traditional cold emails.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Global Reach with Translation Features&lt;/strong&gt;: Use the SDK’s built-in translation and internationalization tools to engage with a global audience. Customize messages to fit local cultures and ensure a personalized experience. Try out this feature in our &lt;a href=&quot;https://img.ly/showcases/cesdk/language/web&quot;&gt;translation demo&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;educational-videos-for-e-learning-platforms&quot;&gt;Educational Videos for E-Learning Platforms&lt;/h3&gt;
&lt;p&gt;CreativeEditor SDK offers users in the e-learning industry an intuitive tool to create engaging and educational videos. &lt;/p&gt;
&lt;p&gt;Educators, teachers, and coaches do not need technical skills, as video creation is simple and accessible. Specifically, users can record, edit, and organize videos across desktop, mobile, or tablet browsers with just a few clicks. &lt;/p&gt;
&lt;p&gt;The SDK simplifies content creation through reusable templates. Instructors can integrate their materials into those template to maintain consistency while covering different topics. Finally, support for &lt;a href=&quot;https://img.ly/docs/cesdk/angular/user-interface/ui-extensions-d194d1/&quot;&gt;interactive plugins&lt;/a&gt;—such as quizzes, polls, and other engagement tools—adds a dynamic layer to video lessons.&lt;/p&gt;
&lt;h3 id=&quot;video-content-for-social-media&quot;&gt;Video Content for Social Media&lt;/h3&gt;
&lt;p&gt;CreativeEditor SDK simplifies the process of producing shareable videos for social media sites like Instagram, TikTok, YouTube, but also LinkedIn and others. That is possible thanks to features like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reusable Templates&lt;/strong&gt;: Start with video templates from other creators or design your own and share them with the community. By choosing from a variety of pre-designed templates for different needs, users can avoid the frustration of starting from scratch and bring their ideas to life quickly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Animations&lt;/strong&gt;: Access a broad &lt;a href=&quot;https://img.ly/docs/cesdk/angular/animation/overview-6a2ef2/&quot;&gt;selection of customizable animations&lt;/a&gt; to enhance the visual appeal of video elements, driving higher engagement.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audio Library&lt;/strong&gt;: Effortlessly incorporate audio files into your videos, such as music, voiceovers, or sound effects, to set the mood and create an immersive experience for viewers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once a video is created in Angular application with CE.SDK, users can export it in the ideal formats and aspect ratios for different social media platforms.&lt;/p&gt;
&lt;h3 id=&quot;showcase-products-on-e-commerce-applications&quot;&gt;Showcase Products on E-Commerce Applications&lt;/h3&gt;
&lt;p&gt;Images have long been a simple and effective way to showcase products on e-commerce platforms. Many sites, from eBay to Amazon, have used them for years. At the same time, customer expectations have evolved, and they now seek more engaging ways to visualize products.&lt;/p&gt;
&lt;p&gt;Short, dynamic product videos significantly enhance product presentation. In particular, Amazon data shows that &lt;a href=&quot;https://sellercentral.amazon.com/seller-forums/discussions/t/b031f21d-e8e2-49ea-933c-bef293bf9427&quot;&gt;adding videos can boost sales by up to 9.7%&lt;/a&gt;. It is no surprise that many Amazon sellers have already adopted video to showcase their products.&lt;/p&gt;
&lt;p&gt;With a CE.SDK-based Angular video editor, you can easily create videos that work seamlessly across multiple devices for a unified shopping experience. Thanks to reusable templates, you can customize product videos while maintaining scalability and brand consistency.By leveraging multiple templates and feeding them with diverse data, you can generate automated product video variations at scale.&lt;/p&gt;
&lt;h3 id=&quot;smart-and-automated-workflows&quot;&gt;Smart and Automated Workflows&lt;/h3&gt;
&lt;p&gt;CreativeEditor SDK supports the development of a tool for creating branded videos at scale.&lt;/p&gt;
&lt;p&gt;As it is based on ready-made and editable templates, it simplifies video production and management. The reason is that users can define these templates once through an intuitive point-and-click interface, then effortlessly customize the produced videos via different input resources or &lt;a href=&quot;https://img.ly/docs/cesdk/angular/create-templates/add-dynamic-content/text-variables-7ecb50/&quot;&gt;configurable text variables&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;That automated workflow process boosts productivity and scalability. The SDK also supports advanced features like &lt;a href=&quot;https://www.oracle.com/cx/marketing/what-is-ab-testing/&quot;&gt;A/B testing&lt;/a&gt;. That way, teams can create multiple versions of a campaign video, distribute them on social platforms, and compare their performance.&lt;/p&gt;
&lt;h2 id=&quot;customizing-your-creativeeditor-sdk-instance&quot;&gt;Customizing your CreativeEditor SDK Instance&lt;/h2&gt;
&lt;p&gt;CreativeEditor SDK features a versatile Angular video editor experience that can be easily configured and customized to fit your specific requirements and visual preferences.Key customization options supported by CE.SDK include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Feature Activation&lt;/strong&gt;: Enable or disable features as you need, so that you can control the functionality available to users.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom UIs&lt;/strong&gt;: Create fully personalized user interfaces, adapting the editor to your unique use case. This includes repositioning toolbar elements, changing icons, or renaming tools for a tailored editing experience.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Internationalization&lt;/strong&gt;: Adapt the video editor for different languages and regions. CE.SDK provides &lt;a href=&quot;https://img.ly/docs/cesdk/angular/user-interface/localization-508e20/&quot;&gt;full support for internationalization&lt;/a&gt;, enabling you to customize all text strings in any language.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Export Options&lt;/strong&gt;: Configure which file formats users can select for exporting, along with settings for quality, dimensions, and more.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Role Customization&lt;/strong&gt;: &lt;a href=&quot;https://img.ly/docs/cesdk/angular/configuration-2c1c3d/&quot;&gt;CE.SDK supports roles&lt;/a&gt;, which are a set of global settings and scopes. When the role changes, the default settings for that role are applied. You can also use a callback to adjust configurations whenever the role changes and define custom roles to tailor settings for specific user needs or workflows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Media Libraries:&lt;/strong&gt; Create custom, sortable media libraries. CE.SDK supports integration with third-party libraries via API, offering easy access to external media assets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Choose the Start Video:&lt;/strong&gt; Set the editor to display a predefined video at the start, eliminating the blank canvas effect.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Theming&lt;/strong&gt;: Adjust the editor’s theme to match your app’s design. You can select from built-in themes, generate new ones using the &lt;a href=&quot;https://img.ly/docs/cesdk/angular/user-interface/appearance/theming-4b0938/&quot;&gt;theme generator&lt;/a&gt;, or manually create a custom theme. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See all of these customizations in action in our &lt;a href=&quot;https://img.ly/showcases/cesdk?tags=customization&quot;&gt;customization demo&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Adding an video editor to your Angular site or web application greatly enhances user experience. Such a feature drives product success by improving user reviews and increasing retention. Whether you are creating a marketing tool, an e-learning or e-commerce platform, or any web app with social media functionality, a video editor adds immense value.&lt;/p&gt;
&lt;p&gt;CreativeEditor SDK gives you what you need to implement a fully customizable video editing solution into your Angular application in minutes.&lt;/p&gt;
&lt;p&gt;By following the steps in this guide, you learned how to equip your users with a professional and intuitive video editing experience. Explore CE.SDK’s video capabilities and &lt;a href=&quot;https://img.ly/docs/cesdk/angular/prebuilt-solutions/video-editor-9e533a/&quot;&gt;dive into the docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Stay tuned for more updates, and please &lt;a href=&quot;https://img.ly/forms/contact-sales?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=plugins1&quot;&gt;reach out&lt;/a&gt; if you have any questions. Thank you for reading.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Over 3,000 creative professionals gain early access to our new features, demos, and updates—don’t miss out, and&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i?ref=img.ly&quot;&gt;&lt;strong&gt;subscribe&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;to our newsletter.&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;</content:encoded><dc:creator>Antonello</dc:creator><media:content url="https://blog.img.ly/2025/01/video-editor-angular.jpg" medium="image"/><category>Angular</category><category>How-To</category><category>Video Editor</category></item><item><title>A Modern Video Editor SDK for Your React Native App</title><link>https://img.ly/blog/a-modern-video-editor-sdk-for-your-react-native-app/</link><guid isPermaLink="true">https://img.ly/blog/a-modern-video-editor-sdk-for-your-react-native-app/</guid><description>Learn how to integrate IMG.LY&apos;s video editor for React Native into your app and how to best leverage its capabilities.</description><pubDate>Mon, 06 Jan 2025 10:40:35 GMT</pubDate><content:encoded>&lt;p&gt;Learn how to integrate &lt;a href=&quot;https://img.ly/docs/cesdk/react-native/prebuilt-solutions/video-editor-9e533a/&quot;&gt;IMG.LY’s video editor for React Native&lt;/a&gt; into your app and how to leverage its capabilities best.&lt;/p&gt;
&lt;h2 id=&quot;why-add-a-video-editor-to-your-react-native-app&quot;&gt;Why Add a Video Editor to Your React Native App?&lt;/h2&gt;
&lt;p&gt;Video content is solidifying its status as the most engaging form of digital media. Platforms like TikTok, Instagram Reels, and YouTube Shorts have made video consumption and creation an engrained habit with billions of users. You can harness this trend by adding a video editor to your app significantly enhancing user engagement and retention.&lt;/p&gt;
&lt;p&gt;React Native, with its ability to create cross-platform applications from a single codebase, is a perfect match for IMG.LY’s &lt;strong&gt;CreativeEditor SDK Video Editor&lt;/strong&gt;. It ensures seamless performance on iOS and Android, powered by the same unified graphics engine across platforms.&lt;/p&gt;
&lt;p&gt;Whether your app focuses on social media, marketing, or eCommerce, integrating a video editor empowers users with a creative tool set while elevating the overall experience.&lt;/p&gt;
&lt;h2 id=&quot;getting-started-integrating-the-video-editor-in-react-native&quot;&gt;Getting Started: Integrating the Video Editor in React Native&lt;/h2&gt;
&lt;h3 id=&quot;requirements&quot;&gt;Requirements&lt;/h3&gt;
&lt;p&gt;Before diving into the integration, ensure your environment meets these requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;React Native&lt;/strong&gt;: 0.73+&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;iOS&lt;/strong&gt;: 16+&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Swift&lt;/strong&gt;: 5.10 (Xcode 15.4)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Android&lt;/strong&gt;: 7+&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To get started, add the &lt;strong&gt;@imgly/editor-react-native&lt;/strong&gt; package to your project:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; @imgly/editor-react-native&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;setting-up-the-editor&quot;&gt;Setting Up the Editor&lt;/h3&gt;
&lt;p&gt;Once the package is installed, initialize the editor by importing the necessary modules and creating an instance of &lt;code&gt;EditorSettingsModel&lt;/code&gt;. You’ll need a license key, which you can obtain from the IMG.LY dashboard.&lt;/p&gt;
&lt;p&gt;Here’s how to set up and launch the video editor:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; IMGLYEditor, { EditorSettingsModel } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@imgly/editor-react-native&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; const&lt;/span&gt;&lt;span&gt; openVideoEditor&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;:&lt;/span&gt;&lt;span&gt; Promise&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt;&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; settings&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; EditorSettingsModel&lt;/span&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;&apos;YOUR_LICENSE_KEY&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; IMGLYEditor.&lt;/span&gt;&lt;span&gt;openEditor&lt;/span&gt;&lt;span&gt;(settings);&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 launches the editor with the &lt;strong&gt;Video Editor&lt;/strong&gt; preset, enabling users to trim, cut, and enhance their videos with filters, text overlays, stickers, and music.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1080px) 1080px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1080&quot; height=&quot;1080&quot; src=&quot;https://img.ly/_astro/Video-UI_Z17RDlV.webp&quot; srcset=&quot;/_astro/Video-UI_Z1hzsFw.webp 640w, /_astro/Video-UI_ZUG5b8.webp 750w, /_astro/Video-UI_Oc4wg.webp 828w, /_astro/Video-UI_Z17RDlV.webp 1080w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;use-cases-building-a-tiktok-like-experience&quot;&gt;Use Cases: Building a TikTok-Like Experience&lt;/h2&gt;
&lt;p&gt;Now that the video editor is integrated into your React Native app, let’s explore some key use cases and how to configure the editor to support them.&lt;/p&gt;
&lt;h3 id=&quot;short-form-video-creation&quot;&gt;Short-Form Video Creation&lt;/h3&gt;
&lt;p&gt;For apps targeting TikTok-style short-form video content, prioritize features that simplify the editing process:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Timeline Control&lt;/strong&gt;: Allow users to trim clips and sync overlays like stickers or music.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Filters &amp;#x26; Effects&lt;/strong&gt;: Offer filters to let users add certain moods to their videos with themes like retro, high contrast, or pastel tones.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Text &amp;#x26; Stickers&lt;/strong&gt;: Add captions and playful stickers for videos that are often consumed on mute.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Music &amp;#x26; Audio&lt;/strong&gt;: Let users add soundtracks or sound effects, with a library of trending music for inspirational background music.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Templates&lt;/strong&gt;: Provide ready-made templates that users can customize for specific occasions or themes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This setup allows influencers and brands to create professional-looking videos that they can share across social media platforms with ease.&lt;/p&gt;
&lt;h3 id=&quot;influencers-and-marketing&quot;&gt;Influencers and Marketing&lt;/h3&gt;
&lt;p&gt;If your app serves influencers or businesses, the ability to create polished, on-brand videos is key. Features to consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Branded Templates&lt;/strong&gt;: Pre-designed templates aligned with specific brand aesthetics, simplifying content creation for marketing campaigns.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Watermarks&lt;/strong&gt;: Add brand logos or watermarks to videos, ensuring creators and brands maintain visibility.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These features empower influencers and businesses to quickly generate content that’s ready for distribution across platforms.&lt;/p&gt;
&lt;h3 id=&quot;e-commerce-and-user-generated-content&quot;&gt;E-commerce and User-Generated Content&lt;/h3&gt;
&lt;p&gt;Video editing is a powerful tool for e-commerce apps, enabling sellers and customers to create engaging product demos, unboxing videos, reviews and tutorials. Features like trimming, filters, and music tools make it easy to produce compelling content that enhances the shopping experience.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vendors can create promotional videos for products.&lt;/li&gt;
&lt;li&gt;Customers can share authentic reviews or tutorials, increasing trust and boosting sales.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;customization-options-with-creativeeditor-sdk&quot;&gt;Customization Options with CreativeEditor SDK&lt;/h2&gt;
&lt;p&gt;The React Native plugin offers a range of customization options to adapt the editor to your app’s needs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;UI Customization&lt;/strong&gt;: Change themes, colors, and layouts to match your app’s branding.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Presets&lt;/strong&gt;: Configure presets for specific use cases, such as limiting video length or optimizing for a particular resolution.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom Assets&lt;/strong&gt;: Add unique filters, stickers, and fonts to create a personalized experience for your audience.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Templates&lt;/strong&gt;: Use the CreativeEditor SDK Web UI to create custom templates, enabling users to start with professional-grade designs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Integrating a video editor into your React Native appwill improve your UX, and help boost engagement, retention, and potential distribution of your product, whether you’re building a social media platform, an influencer tool, or an e-commerce app.&lt;br&gt;
With CreativeEditor SDK, you can create a TikTok-like video editing experience or offer specialized tools for businesses and creators.&lt;/p&gt;
&lt;p&gt;By following the steps in this guide, you can empower your users to create professional-quality video content directly within your app. Explore the full capabilities of the SDK by visiting the &lt;a href=&quot;https://img.ly/docs/cesdk/react-native/prebuilt-solutions/video-editor-9e533a/&quot;&gt;React Native documentation&lt;/a&gt; and getting started today.&lt;/p&gt;
&lt;p&gt;Stay tuned for updates, and don’t hesitate to &lt;a href=&quot;https://img.ly/forms/contact-sales?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=reactnative&quot;&gt;reach out&lt;/a&gt; with any questions.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3,000+ creative professionals gain early access to new features and updates—don’t miss out, and&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i?ref=img.ly&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>Jan</dc:creator><media:content url="https://blog.img.ly/2024/12/how-to-react-native-video-editor-sdk.jpg" medium="image"/><category>React Native</category><category>How-To</category><category>Video Editor</category><category>Expo</category></item><item><title>How to Load Stripe Data into Google BigQuery</title><link>https://img.ly/blog/how-to-load-stripe-data-into-google-bigquery/</link><guid isPermaLink="true">https://img.ly/blog/how-to-load-stripe-data-into-google-bigquery/</guid><description>Discover how IMG.LY leverages Stripe&apos;s Data Pipeline to seamlessly transfer data into Google BigQuery using Google Cloud Functions.</description><pubDate>Thu, 18 Jul 2024 10:04:26 GMT</pubDate><content:encoded>&lt;p&gt;At IMG.LY, we recognize that leveraging data is essential for driving innovation and growth. To optimize our data for reporting, we consolidate multiple data sources, including Stripe billing and financial data, into Google BigQuery.&lt;/p&gt;
&lt;p&gt;IMG.LY is the leading provider of creative editing SDKs for &lt;a href=&quot;https://img.ly/products/video-sdk/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=stripebigquery&quot;&gt;video&lt;/a&gt;, &lt;a href=&quot;https://img.ly/products/photo-sdk/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=stripebigquery&quot;&gt;photo&lt;/a&gt;, and &lt;a href=&quot;https://img.ly/products/creative-sdk/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=stripebigquery&quot;&gt;design templates&lt;/a&gt;. While this article may not directly relate to media creation, we believe in empowering developers through knowledge sharing. Let’s dive in.&lt;/p&gt;
&lt;p&gt;Until now, we’ve relied on &lt;a href=&quot;https://www.fivetran.com&quot;&gt;Fivetran&lt;/a&gt; to fetch our data from Stripe and store it in Google BigQuery. Fivetran uses Stripe’s API, calling each endpoint, iterating over all resources, and storing the results in BigQuery (or any other supported data warehouse). While this generally works well, issues can arise. For instance, we sometimes create Stripe Subscriptions using inline pricing with &lt;a href=&quot;https://docs.stripe.com/api/subscription_items/create#create_subscription_item-price_data&quot;&gt;the &lt;code&gt;price_data&lt;/code&gt; parameter&lt;/a&gt;. This generates a new &lt;code&gt;Price&lt;/code&gt; object in Stripe on-the-fly and immediately sets it to &lt;code&gt;active: false&lt;/code&gt;. Consequently, the &lt;code&gt;Price&lt;/code&gt; object is not returned by Stripe API’s price endpoint, leading to missing data in our warehouse. Although Fivetran’s support was exceptional in resolving this issue within a day, it highlighted a potential flaw in relying solely on ETL services for data extraction.&lt;/p&gt;
&lt;p&gt;Recently, Stripe introduced &lt;a href=&quot;https://stripe.com/data-pipeline&quot;&gt;Data Pipeline&lt;/a&gt;, its own service for transferring Stripe data into a data warehouse. This ensures complete, reliable data without needing a third-party service to read Stripe’s API. Additionally, you can receive test environment data and access several tables not available via the API. For a comprehensive summary of the available data, &lt;a href=&quot;https://dashboard.stripe.com/stripe-schema&quot;&gt;refer to Stripe’s official data schema&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Currently, Stripe supports only Snowflake and Amazon Redshift as data warehouses. However, they’ve recently added the option to &lt;a href=&quot;https://docs.stripe.com/stripe-data/access-data-in-warehouse/cloud-storage/google-cloud-storage&quot;&gt;deliver data as Parquet files into Google Cloud Storage (GCS)&lt;/a&gt;. The next step for us was to import this data into Google BigQuery.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-stripe-data-pipeline-with-google-cloud-storage&quot;&gt;Setting Up Stripe Data Pipeline with Google Cloud Storage&lt;/h2&gt;
&lt;p&gt;Stripe is renowned for its excellent developer experience, and this beta feature is no exception. Enabling it within the Stripe Dashboard is quick, and the &lt;a href=&quot;https://docs.stripe.com/stripe-data/access-data-in-warehouse/cloud-storage/google-cloud-storage&quot;&gt;documentation&lt;/a&gt; is straightforward. After following the instructions and enabling the feature, it takes a while for data to appear in GCS. Once available, a complete data dump is provided every 6 hours, structured as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;At the root level, Stripe creates a folder representing the date and time of the latest transfer, e.g., &lt;code&gt;2024071600&lt;/code&gt; (&lt;code&gt;YYYYMMDDHH&lt;/code&gt;), representing the 12 am push on July 16, 2024.&lt;/li&gt;
&lt;li&gt;One level deeper, there are two folders: &lt;code&gt;livemode&lt;/code&gt; and &lt;code&gt;testmode&lt;/code&gt;, representing live and test data, respectively.&lt;/li&gt;
&lt;li&gt;Each folder contains one folder per data table, e.g., &lt;code&gt;subscriptions&lt;/code&gt; or &lt;code&gt;invoices&lt;/code&gt;. Additionally, a &lt;code&gt;coreapi_SUCCESS&lt;/code&gt; file indicates successful data transfer to your GCS bucket and readiness for consumption.&lt;/li&gt;
&lt;li&gt;Within the table folders are several Parquet files containing the actual data for each table.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;loading-the-data-from-google-cloud-storage-into-google-bigquery&quot;&gt;Loading the Data from Google Cloud Storage into Google BigQuery&lt;/h2&gt;
&lt;p&gt;There are multiple ways to transfer data from GCS to BigQuery. We opted for the following approach:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Using Google Cloud Scheduler to publish a message to Google Pub/Sub every 6 hours at 1 am, 7 am, 1 pm, and 7 pm.&lt;/li&gt;
&lt;li&gt;Creating a Google Cloud Function that listens for new messages on the above Pub/Sub topic. When a message is received, it triggers a Node.js script that loads the most recent data from GCS into BigQuery and deletes it from GCS.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s delve into the details.&lt;/p&gt;
&lt;h3 id=&quot;create-a-google-cloud-scheduler-job&quot;&gt;Create a Google Cloud Scheduler Job&lt;/h3&gt;
&lt;p&gt;First, create a new Cloud Scheduler job &lt;a href=&quot;https://console.cloud.google.com/cloudscheduler/jobs/new&quot;&gt;here&lt;/a&gt; with the following configuration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Name&lt;/strong&gt;: Choose a name for this job.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Region&lt;/strong&gt;: The region is not crucial for this task; we used &lt;code&gt;europe-west3&lt;/code&gt; since most of our services are in Germany.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Frequency&lt;/strong&gt;: We want the job to run every 6 hours at 1 am, 7 am, 1 pm, and 7 pm. Stripe publishes data every 6 hours, but it takes time to transfer it to GCS. We chose 1 hour later than Stripe’s push time, so our value is &lt;code&gt;0 1,7,13,19 * * *&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Timezone&lt;/strong&gt;: Choose ‘Coordinated Universal Time (UTC)’.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Target type&lt;/strong&gt;: Choose ‘Pub/Sub’.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Select a Cloud Pub/Sub topic&lt;/strong&gt;: Select or create a new Pub/Sub topic using the default configuration. This is used to trigger the Cloud Function.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Message body&lt;/strong&gt;: For this task, we don’t look at the contents of the message, as such the content of this value doesn’t matter. We opted for a simple &lt;code&gt;load&lt;/code&gt; string.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 566px) 566px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;566&quot; height=&quot;1240&quot; src=&quot;https://img.ly/_astro/Screenshot-2024-07-16-at-11.46.22_1dcdSQ.webp&quot; srcset=&quot;/_astro/Screenshot-2024-07-16-at-11.46.22_1dcdSQ.webp 566w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Finally, click ‘Create’ to set up the scheduler. Now, a message is published to the selected Pub/Sub topic every 6 hours. Next, we need to respond to this message.&lt;/p&gt;
&lt;h3 id=&quot;create-a-google-cloud-function&quot;&gt;Create a Google Cloud Function&lt;/h3&gt;
&lt;p&gt;Create a Google Cloud Function triggered by Pub/Sub &lt;a href=&quot;https://console.cloud.google.com/functions/add&quot;&gt;here&lt;/a&gt; with the following configuration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Environment&lt;/strong&gt;: Choose ‘2nd gen’.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Function name&lt;/strong&gt;: Choose a name for this Cloud function.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Region&lt;/strong&gt;: Select the region for the function, typically europe-west3 for our services.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trigger type&lt;/strong&gt;: Choose ‘Cloud Pub/Sub’.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloud Pub/Sub topic&lt;/strong&gt;: Select the Pub/Sub topic created in the previous step.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Adjust the ‘Runtime, build, connections and security settings’ based on your Cloud setup and the required processing power for Stripe data. Generally, the following settings work well:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Memory allocated&lt;/strong&gt;: ‘512 MiB’&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CPU&lt;/strong&gt;: ‘1’&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Timeout&lt;/strong&gt;: ‘540’&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Minimum number of instances&lt;/strong&gt;: ‘0’ (to ensure the function shuts down when not in use)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maximum number of instances&lt;/strong&gt;: ‘1’&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service account&lt;/strong&gt;: Use or create a service account with permissions to access the GCS bucket where Stripe data is stored and the BigQuery datasets to load the data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ingress settings&lt;/strong&gt;: Choose ‘Allow internal traffic only’.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 554px) 554px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;554&quot; height=&quot;1286&quot; src=&quot;https://img.ly/_astro/Screenshot-2024-07-16-at-11.56.55_1Iq5S7.webp&quot; srcset=&quot;/_astro/Screenshot-2024-07-16-at-11.56.55_1Iq5S7.webp 554w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Click ‘Next’ to provide the function’s code. Select:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Runtime&lt;/strong&gt;: ‘Node.js 20’&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Source code&lt;/strong&gt;: ‘Inline Editor’&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Entry point&lt;/strong&gt;: &lt;code&gt;loadStripeData&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the &lt;code&gt;package.json&lt;/code&gt;, add the BigQuery and Cloud Storage Node.js packages:&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;index.js&lt;/code&gt;, add the following code:&lt;/p&gt;

&lt;p&gt;This script does the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;For each environment (live and test), it searches for the latest folder containing a &lt;code&gt;coreapi_SUCCESS&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;For each table, it groups all related Parquet files and loads them into the BigQuery table using &lt;code&gt;WRITE_TRUNCATE&lt;/code&gt;, which overwrites existing data. Note that the location is specified as &lt;code&gt;EU&lt;/code&gt;, matching our BigQuery dataset and GCS bucket location. Adjust this parameter if your data is elsewhere.&lt;/li&gt;
&lt;li&gt;If all files for an environment are loaded without errors, the files are deleted from GCS. This step is optional; if you prefer to keep a backup, you can omit this part.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Click ‘Deploy’ to deploy your Cloud function.&lt;/p&gt;
&lt;h3 id=&quot;create-bigquery-datasets&quot;&gt;Create BigQuery Datasets&lt;/h3&gt;
&lt;p&gt;The final step is to create two datasets in Google BigQuery. Open &lt;a href=&quot;https://console.cloud.google.com/bigquery&quot;&gt;Google BigQuery&lt;/a&gt;, click on the three dots next to your project’s name, and select ‘Create dataset’. Enter a name and choose a location matching your GCS bucket’s location. Repeat this process for the test dataset.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Following these steps will ensure your Stripe data is imported into BigQuery and automatically updated every 6 hours. However, as Data Pipeline for GCS is still in beta, there are some limitations. For example, the schema of the Parquet files lacks type annotations for timestamps, so all timestamps in BigQuery are represented as &lt;code&gt;INTEGER&lt;/code&gt; instead of &lt;code&gt;TIMESTAMP&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Additionally, some tables, such as &lt;code&gt;subscription_item_change_events&lt;/code&gt;, are not currently transferred when syncing with Google Cloud Storage, although this issue is expected to be resolved soon. Meanwhile, we continue to use Fivetran in conjunction with the above method to sync Stripe data to Google BigQuery and plan to fully migrate to Data Pipeline once it exits the beta phase.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thank you for reading!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3,000+ creative professionals gain exclusive access and hear of our releases first—&lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;subscribe&lt;/a&gt; to our newsletter and never miss out.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Sascha</dc:creator><media:content url="https://blog.img.ly/2024/07/stripe-bigquery-how-to.jpg" medium="image"/><category>How-To</category><category>Business Intelligence</category><category>Data</category><category>Cloud</category><category>Insights</category></item><item><title>Elegant Event Handling in Kotlin - A Refactoring Walkthrough</title><link>https://img.ly/blog/kotlin-code-refactoring-for-peak-performance/</link><guid isPermaLink="true">https://img.ly/blog/kotlin-code-refactoring-for-peak-performance/</guid><description>Unlock peak performance in Kotlin code with these expert refactoring tips.</description><pubDate>Fri, 29 Sep 2023 07:00:53 GMT</pubDate><content:encoded>&lt;p&gt;In the world of software development, code refactoring is the hero that rescues us from tangled and inefficient code. In this article, we’ll embark on an adventure to revamp Kotlin code handling diverse events. Our mission? To enhance performance and style, making the code sleeker, more maintainable, and a joy to work with.&lt;/p&gt;
&lt;h2 id=&quot;what-we-aim-to-achieve&quot;&gt;What We Aim to Achieve&lt;/h2&gt;
&lt;p&gt;On this journey to transform Kotlin event handling, our goal is to refine our code to be more efficient, readable, and maintainable. We’re introducing a variety of improvements, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Replacing a convoluted &lt;code&gt;when&lt;/code&gt; statement with a &lt;code&gt;HashMap&lt;/code&gt; for lightning-fast (O(1)) performance.&lt;/li&gt;
&lt;li&gt;Infusing syntactic sweetness with inline functions and &lt;a href=&quot;https://kotlinlang.org/docs/inline-functions.html#reified-type-parameters&quot;&gt;reified&lt;/a&gt; type parameters.&lt;/li&gt;
&lt;li&gt;Employing delegated properties for cleaner dependency injection.&lt;/li&gt;
&lt;li&gt;Adhering to the Single Responsibility Principle by enabling multiple specialized event handler functions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;step-1-the-starting-line&quot;&gt;Step 1: The Starting Line&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;sven2244_a_playful_abstract_artwork_that_represents_the_complex_6e9491ff-8203-4ae2-9a5e-52025e4010be.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1856px) 1856px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1856&quot; height=&quot;656&quot; src=&quot;https://img.ly/_astro/2_1H0FvJ.webp&quot; srcset=&quot;/_astro/2_Z2e3gQ7.webp 640w, /_astro/2_2dHdPv.webp 750w, /_astro/2_ZB5tWb.webp 828w, /_astro/2_Z1Ro7yX.webp 1080w, /_astro/2_Qgvw5.webp 1280w, /_astro/2_NDMYj.webp 1668w, /_astro/2_1H0FvJ.webp 1856w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Our adventure begins with a glance at the original code. This codebase manages a variety of block events through a function named &lt;code&gt;handleBlockEvent&lt;/code&gt; and an event handler function called &lt;code&gt;onEvent&lt;/code&gt;. Let’s unveil the original code:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onEvent&lt;/span&gt;&lt;span&gt;(event: &lt;/span&gt;&lt;span&gt;Event&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;    handleBlockEvent&lt;/span&gt;&lt;span&gt;(engine, &lt;/span&gt;&lt;span&gt;getBlockForEvents&lt;/span&gt;&lt;span&gt;(), &lt;/span&gt;&lt;span&gt;checkNotNull&lt;/span&gt;&lt;span&gt;(assetsRepo.fontFamilies.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;getOrThrow&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;fun&lt;/span&gt;&lt;span&gt; handleBlockEvent&lt;/span&gt;&lt;span&gt;(engine: &lt;/span&gt;&lt;span&gt;Engine&lt;/span&gt;&lt;span&gt;, block: &lt;/span&gt;&lt;span&gt;DesignBlock&lt;/span&gt;&lt;span&gt;, fontFamilyMap: &lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;FontFamilyData&lt;/span&gt;&lt;span&gt;&gt;, event: &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    when&lt;/span&gt;&lt;span&gt; (event) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnDelete &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; engine.&lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnBackward &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; engine.&lt;/span&gt;&lt;span&gt;sendBackward&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnDuplicate &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; engine.&lt;/span&gt;&lt;span&gt;duplicate&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnForward &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; engine.&lt;/span&gt;&lt;span&gt;bringForward&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.ToBack &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; engine.&lt;/span&gt;&lt;span&gt;sendToBack&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.ToFront &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; engine.&lt;/span&gt;&lt;span&gt;bringToFront&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnChangeFinish &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; engine.editor.&lt;/span&gt;&lt;span&gt;addUndoStep&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        is&lt;/span&gt;&lt;span&gt; BlockEvent.OnChangeBlendMode &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; onChangeBlendMode&lt;/span&gt;&lt;span&gt;(engine, block, event.blendMode)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        is&lt;/span&gt;&lt;span&gt; BlockEvent.OnChangeOpacity &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;setOpacity&lt;/span&gt;&lt;span&gt;(block, event.opacity)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        is&lt;/span&gt;&lt;span&gt; BlockEvent.OnChangeFillColor &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; onChangeFillColor&lt;/span&gt;&lt;span&gt;(engine, block, event.color)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // and so on...&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;sealed&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; BlockEvent&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    object&lt;/span&gt;&lt;span&gt; OnChangeFinish&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    object&lt;/span&gt;&lt;span&gt; OnForward&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    object&lt;/span&gt;&lt;span&gt; OnBackward&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    object&lt;/span&gt;&lt;span&gt; OnDuplicate&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    object&lt;/span&gt;&lt;span&gt; OnDelete&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    object&lt;/span&gt;&lt;span&gt; ToFront&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    object&lt;/span&gt;&lt;span&gt; ToBack&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    data&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; OnChangeBlendMode&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; blendMode: &lt;/span&gt;&lt;span&gt;BlendMode&lt;/span&gt;&lt;span&gt;) : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    data&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; OnChangeOpacity&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; opacity: &lt;/span&gt;&lt;span&gt;Float&lt;/span&gt;&lt;span&gt;) : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    data&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; OnChangeFillColor&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; color: &lt;/span&gt;&lt;span&gt;Color&lt;/span&gt;&lt;span&gt;) : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // and so on...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To use the original code, you’d typically call the &lt;code&gt;onEvent&lt;/code&gt; function with a specific event:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;onEvent&lt;/span&gt;&lt;span&gt;(BlockEvent.&lt;/span&gt;&lt;span&gt;OnChangeFillColor&lt;/span&gt;&lt;span&gt;(Color.RED))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would then trigger the &lt;code&gt;handleBlockEvent&lt;/code&gt; function to deal with the event at hand. Now, let’s embark on our first refactoring adventure.&lt;/p&gt;
&lt;h2 id=&quot;step-2-unveiling-hashmaps-and-payloads-for-peak-performance&quot;&gt;Step 2: Unveiling HashMaps and Payloads for Peak Performance&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;sven2244_an_abstract_image_ofa_futuristic_cityscape_with_neon-l_fbda2c5b-b80f-430b-9f00-609ceecc1292.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1856px) 1856px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1856&quot; height=&quot;656&quot; src=&quot;https://img.ly/_astro/3_ZK0M1r.webp&quot; srcset=&quot;/_astro/3_Z2iStzt.webp 640w, /_astro/3_28R179.webp 750w, /_astro/3_ZFUGFx.webp 828w, /_astro/3_JLxGM.webp 1080w, /_astro/3_Z1AJW16.webp 1280w, /_astro/3_Z1DmExR.webp 1668w, /_astro/3_ZK0M1r.webp 1856w&quot;&gt;&lt;/p&gt;
&lt;p&gt;In our first act of refactoring, we introduce a trusty &lt;code&gt;HashMap&lt;/code&gt; to map each event type to its corresponding action. This heroic move eliminates the need for the convoluted &lt;code&gt;when&lt;/code&gt; statement, making our code more efficient. We also unveil a payload mechanism to convey essential data to the event handlers.&lt;/p&gt;
&lt;p&gt;Behold the refactored code:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;abstract&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; fillPayload: (&lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;) -&gt; &lt;/span&gt;&lt;span&gt;Unit&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;    abstract&lt;/span&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; payloadCache: &lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    private&lt;/span&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; eventMap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; mutableMapOf&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;KClass&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;out Event&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;, Payloads.(event: &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Unit&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 class=&quot;line&quot;&gt;&lt;span&gt;    fun&lt;/span&gt;&lt;span&gt; handleEvent&lt;/span&gt;&lt;span&gt;(event: &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        eventMap[event::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;]?.&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            it.&lt;/span&gt;&lt;span&gt;invoke&lt;/span&gt;&lt;span&gt;(payloadCache.&lt;/span&gt;&lt;span&gt;also&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;fillPayload&lt;/span&gt;&lt;span&gt;(it) }, event)&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;    operator&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; &amp;#x3C;&lt;/span&gt;&lt;span&gt;EventType&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(event: &lt;/span&gt;&lt;span&gt;KClass&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt; EventType&lt;/span&gt;&lt;span&gt;&gt;, lambda: &lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;.(event: &lt;/span&gt;&lt;span&gt;EventType&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Unit) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        eventMap[event] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; lambda &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; Payloads.(event: &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Unit&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;class&lt;/span&gt;&lt;span&gt; BlockEventsHandler&lt;/span&gt;&lt;span&gt;(fillPayload: (&lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;BlockEventsHandler&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;) -&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt;) : &lt;/span&gt;&lt;span&gt;EventsHandler&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEventsHandler&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;&gt;(&lt;/span&gt;&lt;span&gt;fillPayload&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    class&lt;/span&gt;&lt;span&gt; Payloads&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        lateinit&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt; engine: &lt;/span&gt;&lt;span&gt;Engine&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        lateinit&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt; block: &lt;/span&gt;&lt;span&gt;DesignBlock&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        lateinit&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt; fontFamilyMap: &lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;FontFamilyData&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;    override&lt;/span&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; payloadCache: &lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Payloads&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;    init&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.OnDelete::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { engine.&lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.OnBackward::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { engine.&lt;/span&gt;&lt;span&gt;sendBackward&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.OnDuplicate::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { engine.&lt;/span&gt;&lt;span&gt;duplicate&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.OnForward::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { engine.&lt;/span&gt;&lt;span&gt;bringForward&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.ToBack::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { engine.&lt;/span&gt;&lt;span&gt;sendToBack&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.ToFront::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { engine.&lt;/span&gt;&lt;span&gt;bringToFront&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.OnChangeFinish::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { engine.editor.&lt;/span&gt;&lt;span&gt;addUndoStep&lt;/span&gt;&lt;span&gt;() }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.OnChangeBlendMode::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;onChangeBlendMode&lt;/span&gt;&lt;span&gt;(engine, block, it.blendMode) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.OnChangeOpacity::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { engine.block.&lt;/span&gt;&lt;span&gt;setOpacity&lt;/span&gt;&lt;span&gt;(block, it.opacity) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.OnChangeFillColor::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;onChangeFillColor&lt;/span&gt;&lt;span&gt;(engine, block, it.color) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // and so on...&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;private&lt;/span&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; blockEventHandler &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; BlockEventsHandler&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    it.engine &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; engine&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    it.block &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; getBlockForEvents&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    it.fontFamilyMap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; checkNotNull&lt;/span&gt;&lt;span&gt;(assetsRepo.fontFamilies.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;getOrThrow&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;open&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onEvent&lt;/span&gt;&lt;span&gt;(event: &lt;/span&gt;&lt;span&gt;Event&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;    blockEventHandler.&lt;/span&gt;&lt;span&gt;handleEvent&lt;/span&gt;&lt;span&gt;(event)&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;h3 id=&quot;a-performance-boost&quot;&gt;A Performance Boost&lt;/h3&gt;
&lt;p&gt;By harnessing the power of a &lt;code&gt;HashMap&lt;/code&gt;, we’ve turbocharged our event handling. The time complexity for handling an event is now a lightning-fast (O(1)), a monumental improvement over the (O(n)) time complexity of the ponderous &lt;code&gt;when&lt;/code&gt; statement. While our payload mechanism adds a dollop of syntactic sugar. It enables us to bundle all the necessary data into a single object, making our code more legible and maintainable.&lt;/p&gt;
&lt;p&gt;? Note: Using a HashMap instead of a large when() statement provides a significant performance improvement. It can be up to 40 to 150 times faster. However, explaining the details would exceed the scope of this blog post. Therefore, I will cover it, along with other Kotlin performance puzzles, in a future blog post.&lt;/p&gt;
&lt;p&gt;While the refactored code remains as simple as before:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;onEvent&lt;/span&gt;&lt;span&gt;(BlockEvent.&lt;/span&gt;&lt;span&gt;OnChangeFillColor&lt;/span&gt;&lt;span&gt;(Color.RED))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This still triggers the &lt;code&gt;handleEvent&lt;/code&gt; method in &lt;code&gt;BlockEventsHandler&lt;/code&gt;, which in turn performs the appropriate action based on the event type. The &lt;code&gt;BlockEvent&lt;/code&gt; itself is a data object containing all event details, and it serves as the lambda parameter.&lt;/p&gt;
&lt;h3 id=&quot;a-note-on-payloads&quot;&gt;A Note on Payloads&lt;/h3&gt;
&lt;p&gt;The payload creation is a dynamic lambda function that’s executed each time an event is handled. This ensures that all variables not part of the event are consistently up-to-date. Given that we’re dealing with a single thread per event handler, caching the payload is entirely secure.&lt;/p&gt;
&lt;h2 id=&quot;step-3-adding-syntactic-sweetness-with-infix-functions&quot;&gt;Step 3: Adding Syntactic Sweetness with Infix Functions&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;sven2244_an_abstract_composition_resembling_a_candy_store_fille_894e8c68-0a37-4ad0-adc8-c313db1c29da.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1856px) 1856px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1856&quot; height=&quot;656&quot; src=&quot;https://img.ly/_astro/4_1R9Sfj.webp&quot; srcset=&quot;/_astro/4_Z2nIGiP.webp 640w, /_astro/4_241NnM.webp 750w, /_astro/4_ZKKToT.webp 828w, /_astro/4_Z1HeTPo.webp 1080w, /_astro/4_11pIfE.webp 1280w, /_astro/4_XN0HS.webp 1668w, /_astro/4_1R9Sfj.webp 1856w&quot;&gt;&lt;/p&gt;
&lt;p&gt;In our next act, we elevate our syntax to a new level of expressiveness and readability. We introduce an infix function called &lt;code&gt;to&lt;/code&gt;, allowing us to map an event class to its corresponding action elegantly.&lt;/p&gt;
&lt;p&gt;Witness the updated code:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;abstract&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; fillPayload: (&lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;) -&gt; &lt;/span&gt;&lt;span&gt;Unit&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;    infix&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; &amp;#x3C;&lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EventType&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;KClass&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;out EventType&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;(lambda: &lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;.(event: &lt;/span&gt;&lt;span&gt;EventType&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Unit) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        eventMap[event] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; lambda &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; Payloads.(event: &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Unit&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;    // ... (rest of the code remains the same)&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;class&lt;/span&gt;&lt;span&gt; BlockEventsHandler&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    manager: &lt;/span&gt;&lt;span&gt;EventsManager&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    override&lt;/span&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; fillPayload: (&lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;TextBlockEventsHandler&lt;/span&gt;&lt;span&gt;) -&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;) : &lt;/span&gt;&lt;span&gt;EventsHandler&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;TextBlockEventsHandler&lt;/span&gt;&lt;span&gt;&gt;(&lt;/span&gt;&lt;span&gt;manager&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    lateinit&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt; engine: &lt;/span&gt;&lt;span&gt;Engine&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    lateinit&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt; block: &lt;/span&gt;&lt;span&gt;DesignBlock&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    lateinit&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt; fontFamilyMap: &lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;FontFamilyData&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 class=&quot;line&quot;&gt;&lt;span&gt;    init&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnDelete::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.&lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt;(block)&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;        BlockEvent.OnBackward::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.&lt;/span&gt;&lt;span&gt;sendBackward&lt;/span&gt;&lt;span&gt;(block)&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;        BlockEvent.OnDuplicate::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.&lt;/span&gt;&lt;span&gt;duplicate&lt;/span&gt;&lt;span&gt;(block)&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;        BlockEvent.OnForward::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.&lt;/span&gt;&lt;span&gt;bringForward&lt;/span&gt;&lt;span&gt;(block)&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;        BlockEvent.ToBack::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.&lt;/span&gt;&lt;span&gt;sendToBack&lt;/span&gt;&lt;span&gt;(block)&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;        BlockEvent.ToFront::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.&lt;/span&gt;&lt;span&gt;bringToFront&lt;/span&gt;&lt;span&gt;(block)&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;        BlockEvent.OnChangeFinish::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.editor.&lt;/span&gt;&lt;span&gt;addUndoStep&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;        BlockEvent.OnChangeBlendMode::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            onChangeBlendMode&lt;/span&gt;&lt;span&gt;(engine, block, it.blendMode)&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;        BlockEvent.OnChangeOpacity::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.block.&lt;/span&gt;&lt;span&gt;setOpacity&lt;/span&gt;&lt;span&gt;(block, it.opacity)&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;        BlockEvent.OnChangeFillColor::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            onChangeFillColor&lt;/span&gt;&lt;span&gt;(engine, block, it.color)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;syntactic-sweetness-and-performance&quot;&gt;Syntactic Sweetness and Performance&lt;/h3&gt;
&lt;p&gt;The introduction of the &lt;code&gt;to&lt;/code&gt; infix function adds a sprinkle of syntactic sweetness that enhances code expressiveness and enables a more natural usage. This makes it crystal clear what each event is all about. And fear not, the performance remains at a blazing-fast (O(1)), thanks to our trusty HashMap.&lt;/p&gt;
&lt;h3 id=&quot;flexibility-in-syntax&quot;&gt;Flexibility in Syntax&lt;/h3&gt;
&lt;p&gt;While the &lt;code&gt;to&lt;/code&gt; keyword is used here, feel free to substitute it with other terms like &lt;code&gt;handle&lt;/code&gt;, &lt;code&gt;trigger&lt;/code&gt;, or anything that best suits your context. Flexibility is the name of the game.&lt;/p&gt;
&lt;h2 id=&quot;step-4-embracing-inline-functions-for-elegance&quot;&gt;Step 4: Embracing Inline Functions for Elegance&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;sven2244_a_mesmerizing_image_of_a_kaleidoscope_with_vibrant_pat_b53a7e30-820a-4308-a7aa-11222be8a7a1.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1856px) 1856px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1856&quot; height=&quot;656&quot; src=&quot;https://img.ly/_astro/5_ZzQzhR.webp&quot; srcset=&quot;/_astro/5_Z2syT2c.webp 640w, /_astro/5_1YbAEq.webp 750w, /_astro/5_ZPB78g.webp 828w, /_astro/5_TUKqm.webp 1080w, /_astro/5_Z1qAJhw.webp 1280w, /_astro/5_Z1tdrOi.webp 1668w, /_astro/5_ZzQzhR.webp 1856w&quot;&gt;&lt;/p&gt;
&lt;p&gt;However, this is still not perfect because the &lt;code&gt;::class&lt;/code&gt; breaks smooth reading.&lt;/p&gt;
&lt;p&gt;So let’s do it differently. Let us try to introduce a more elegant way to register an event. Let us eliminate the need to specify &lt;code&gt;::class&lt;/code&gt; every time we register an event handler will make our code more concise and readable.&lt;/p&gt;
&lt;p&gt;This is made possible by an inline function with a verified type parameter that maintains the class reference at runtime.&lt;/p&gt;
&lt;p&gt;To do this, we extend the &lt;code&gt;EventsHandler&lt;/code&gt; class with this new &lt;code&gt;register&lt;/code&gt; function:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    register: &lt;/span&gt;&lt;span&gt;EventsHandler&lt;/span&gt;&lt;span&gt;.() &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Unit,&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;    inline&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; &amp;#x3C;&lt;/span&gt;&lt;span&gt;reified&lt;/span&gt;&lt;span&gt; EventType&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BaseEvent&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;register&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;noinline&lt;/span&gt;&lt;span&gt; lambda: (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;EventType&lt;/span&gt;&lt;span&gt;) -&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt;) : &lt;/span&gt;&lt;span&gt;Any&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        this&lt;/span&gt;&lt;span&gt;[EventType::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; lambda&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; lambda&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;   // ... (rest of the code remains the same)&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;h3 id=&quot;the-new-syntax&quot;&gt;The New Syntax&lt;/h3&gt;
&lt;p&gt;This is what registering an event handler looks like with the new syntax:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OnChangeLineWidth&lt;/span&gt;&lt;span&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    engine.block.&lt;/span&gt;&lt;span&gt;setWidth&lt;/span&gt;&lt;span&gt;(block, engine.block.&lt;/span&gt;&lt;span&gt;getFrameWidth&lt;/span&gt;&lt;span&gt;(block))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    engine.block.&lt;/span&gt;&lt;span&gt;setHeight&lt;/span&gt;&lt;span&gt;(block, it.width)&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;Much better, right? The new syntax is more concise, eliminates redundancy, and is type-safe because the reified type parameters ensure that the event type is known at compile-time and runtime, eliminating the need for unsafe casting.&lt;/p&gt;
&lt;h2 id=&quot;step-5-elevating-register-to-an-extension-function-for-highlighting&quot;&gt;Step 5: Elevating &lt;code&gt;register&lt;/code&gt; to an Extension Function for Highlighting&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;highlighting.jpg&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2000px) 2000px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2000&quot; height=&quot;700&quot; src=&quot;https://img.ly/_astro/6-1_ZpUYI8.webp&quot; srcset=&quot;/_astro/6-1_1WCnM8.webp 640w, /_astro/6-1_Z2lJRV7.webp 750w, /_astro/6-1_1HMAQd.webp 828w, /_astro/6-1_ZlU4Yo.webp 1080w, /_astro/6-1_Z2ruRRK.webp 1280w, /_astro/6-1_Z29YNlp.webp 1668w, /_astro/6-1_ZpUYI8.webp 2000w&quot;&gt;&lt;/p&gt;
&lt;p&gt;To improve code readability, we’ll make a subtle but effective step by converting the &lt;code&gt;register&lt;/code&gt; function from a &lt;code&gt;EventsHandler&lt;/code&gt; class function, into an &lt;code&gt;EventsHandler&lt;/code&gt; extension function.&lt;/p&gt;
&lt;p&gt;Sounds stupid! So why?&lt;/p&gt;
&lt;p&gt;This small change improves code readability by highlighting the &lt;code&gt;register&lt;/code&gt; keyword through syntax highlighting from a Kotlin extension function. This will make it much more colorful, which improves readability.&lt;/p&gt;
&lt;h3 id=&quot;updated-eventshandler-class&quot;&gt;Updated &lt;code&gt;EventsHandler&lt;/code&gt; Class&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;EventsHandler&lt;/code&gt; class remains largely unchanged, but the &lt;code&gt;register&lt;/code&gt; function is now outside the class and transformed into an extension function for the &lt;code&gt;EventsHandler&lt;/code&gt; class:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    register: &lt;/span&gt;&lt;span&gt;EventsHandler&lt;/span&gt;&lt;span&gt;.() &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Unit,&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;    // ... (rest of the code remains the same)&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;inline&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; &amp;#x3C;&lt;/span&gt;&lt;span&gt;reified&lt;/span&gt;&lt;span&gt; EventType&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BaseEvent&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;EventsHandler&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;register&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;noinline&lt;/span&gt;&lt;span&gt; lambda: (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;EventType&lt;/span&gt;&lt;span&gt;) -&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt;) : &lt;/span&gt;&lt;span&gt;Any&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    this&lt;/span&gt;&lt;span&gt;[EventType::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; lambda&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; lambda&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;By simply shifting &lt;code&gt;register&lt;/code&gt; out of the class, the &lt;code&gt;EventsHandler&lt;/code&gt; class definition now stands out with distinctive syntax highlighting. It’s a clever trick that doesn’t impact runtime or compile performance, since it’s an inline operation anyway.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;register&lt;/span&gt;&lt;span&gt;**&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent.OnChangeLineWidth&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;    engine.block.&lt;/span&gt;&lt;span&gt;setWidth&lt;/span&gt;&lt;span&gt;(block, engine.block.&lt;/span&gt;&lt;span&gt;getFrameWidth&lt;/span&gt;&lt;span&gt;(block))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    engine.block.&lt;/span&gt;&lt;span&gt;setHeight&lt;/span&gt;&lt;span&gt;(block, it.width)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;step-6-eliminating-lateinit-variables-with-delegated-properties&quot;&gt;Step 6: Eliminating &lt;code&gt;lateinit&lt;/code&gt; Variables with Delegated Properties&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;sven2244_an_abstract_image_of_interconnected_gears_and_cogs_for_3b651ffc-9367-470d-8be3-82c3c19b9ffe.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1856px) 1856px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1856&quot; height=&quot;656&quot; src=&quot;https://img.ly/_astro/7_ZpHmyi.webp&quot; srcset=&quot;/_astro/7_2rVOk1.webp 640w, /_astro/7_1OvbcH.webp 750w, /_astro/7_Z10hwzY.webp 828w, /_astro/7_154X9V.webp 1080w, /_astro/7_Z1grwxW.webp 1280w, /_astro/7_Z1j4f5I.webp 1668w, /_astro/7_ZpHmyi.webp 1856w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now, it’s time to address the enigmatic &lt;code&gt;lateinit&lt;/code&gt; variables and the somewhat convoluted &lt;code&gt;fillPayload&lt;/code&gt; mechanism. Let us introduce a cleaner approach, using delegated properties and lambda functions to inject dependencies.&lt;/p&gt;
&lt;p&gt;Let’s add an &lt;code&gt;Inject&lt;/code&gt; class to wrap a normal lambda as delegable:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Type&lt;/span&gt;&lt;span&gt;&gt;(&lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; inject: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Type) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    operator&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; getValue&lt;/span&gt;&lt;span&gt;(thisRef: &lt;/span&gt;&lt;span&gt;Any&lt;/span&gt;&lt;span&gt;?, property: &lt;/span&gt;&lt;span&gt;KProperty&lt;/span&gt;&lt;span&gt;&amp;#x3C;*&gt;): &lt;/span&gt;&lt;span&gt;Type&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; inject&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this newfound power, our event handler code becomes cleaner and more intuitive. It takes on the style of Jetpack Compose’s declarative syntax:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fun&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;textBlockEvents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    engine: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Engine,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    block: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; DesignBlock,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    fontFamilyMap: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Map&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;String, FontFamilyData&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;    // Inject the dependencies&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; engine &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(engine)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; block &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; fontFamilyMap &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(fontFamilyMap)&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;    // Event handling logic here&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;Whenever one of the variables is accessed, the lambda is called, and you always get the current variable.&lt;/p&gt;
&lt;p&gt;Also, the creation of the “payload” becomes more straightforward, clean, and type-safe. It kinda looks like passing a variable:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; eventHandler &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    textBlockEvents&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        engine &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ::&lt;/span&gt;&lt;span&gt;engine&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        block &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ::&lt;/span&gt;&lt;span&gt;getBlockForEvents&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        fontFamilyMap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;checkNotNull&lt;/span&gt;&lt;span&gt;(assetsRepo.fontFamilies.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;getOrThrow&lt;/span&gt;&lt;span&gt;() },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looks and feels like magic! Pretty cool, right?&lt;/p&gt;
&lt;h2 id=&quot;step-7-multiple-event-handlers-for-single-responsibility-principle&quot;&gt;Step 7: Multiple Event Handlers for Single Responsibility Principle&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;sven2244_code_as_a_magical_candy-filled_wonderland._Syntax_Suga_a50bcd92-bea2-4fea-aa92-e1cd557277b6.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1856px) 1856px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1856&quot; height=&quot;656&quot; src=&quot;https://img.ly/_astro/8_2csiHs.webp&quot; srcset=&quot;/_astro/8_2n6BAE.webp 640w, /_astro/8_1JEXtl.webp 750w, /_astro/8_Z157Jjl.webp 828w, /_astro/8_Z1mVunf.webp 1080w, /_astro/8_1lI8HN.webp 1280w, /_astro/8_1j6qb2.webp 1668w, /_astro/8_2csiHs.webp 1856w&quot;&gt;&lt;/p&gt;
&lt;p&gt;In our grand finale, we harness the newfound flexibility from our previous changes to register multiple event handler functions. Each event handler registration function now has a specific topic, aligning perfectly with the Single Responsibility Principle (SRP).&lt;/p&gt;
&lt;h3 id=&quot;enhanced-event-handler-registration&quot;&gt;Enhanced Event Handler Registration&lt;/h3&gt;
&lt;p&gt;We can now register multiple event handler functions within the same &lt;code&gt;EventsHandler&lt;/code&gt; instance. Each function can specialize in handling a particular type of event, making the code more modular and manageable. Behold the grand design:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; eventHandler &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    cropEvents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        engine &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ::&lt;/span&gt;&lt;span&gt;engine&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        block &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ::&lt;/span&gt;&lt;span&gt;getBlockForEvents&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;    blockEvents&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        engine &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ::&lt;/span&gt;&lt;span&gt;engine&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        block &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ::&lt;/span&gt;&lt;span&gt;getBlockForEvents&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;    textBlockEvents&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        engine &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ::&lt;/span&gt;&lt;span&gt;engine&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        block &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ::&lt;/span&gt;&lt;span&gt;getBlockForEvents&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        fontFamilyMap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;checkNotNull&lt;/span&gt;&lt;span&gt;(assetsRepo.fontFamilies.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;getOrThrow&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 class=&quot;line&quot;&gt;&lt;span&gt;fun&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;blockEvents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    engine: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Engine,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    block: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; DesignBlock&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;    val&lt;/span&gt;&lt;span&gt; engine: &lt;/span&gt;&lt;span&gt;Engine&lt;/span&gt;&lt;span&gt; by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;engine&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; block: &lt;/span&gt;&lt;span&gt;DesignBlock&lt;/span&gt;&lt;span&gt; by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;block&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;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OnDelete&lt;/span&gt;&lt;span&gt;&gt; { engine.&lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt;(block) }&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;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OnBackward&lt;/span&gt;&lt;span&gt;&gt; { engine.&lt;/span&gt;&lt;span&gt;sendBackward&lt;/span&gt;&lt;span&gt;(block) }&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;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OnDuplicate&lt;/span&gt;&lt;span&gt;&gt; { engine.&lt;/span&gt;&lt;span&gt;duplicate&lt;/span&gt;&lt;span&gt;(block) }&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;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OnForward&lt;/span&gt;&lt;span&gt;&gt; { engine.&lt;/span&gt;&lt;span&gt;bringForward&lt;/span&gt;&lt;span&gt;(block) }&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;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ToBack&lt;/span&gt;&lt;span&gt;&gt; { engine.&lt;/span&gt;&lt;span&gt;sendToBack&lt;/span&gt;&lt;span&gt;(block) }&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;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ToFront&lt;/span&gt;&lt;span&gt;&gt; { engine.&lt;/span&gt;&lt;span&gt;bringToFront&lt;/span&gt;&lt;span&gt;(block) }&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;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OnChangeFinish&lt;/span&gt;&lt;span&gt;&gt; { engine.editor.&lt;/span&gt;&lt;span&gt;addUndoStep&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;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OnChangeBlendMode&lt;/span&gt;&lt;span&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        if&lt;/span&gt;&lt;span&gt; (engine.block.&lt;/span&gt;&lt;span&gt;getBlendMode&lt;/span&gt;&lt;span&gt;(block) &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; it.blendMode) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.block.&lt;/span&gt;&lt;span&gt;setBlendMode&lt;/span&gt;&lt;span&gt;(block, it.blendMode)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.editor.&lt;/span&gt;&lt;span&gt;addUndoStep&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;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OnChangeOpacity&lt;/span&gt;&lt;span&gt;&gt; { engine.block.&lt;/span&gt;&lt;span&gt;setOpacity&lt;/span&gt;&lt;span&gt;(block, it.opacity) }&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;fun&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cropEvents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    engine: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Engine,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    block: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; DesignBlock&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;    val&lt;/span&gt;&lt;span&gt; engine: &lt;/span&gt;&lt;span&gt;Engine&lt;/span&gt;&lt;span&gt; by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;engine&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; block: &lt;/span&gt;&lt;span&gt;DesignBlock&lt;/span&gt;&lt;span&gt; by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // ... (event handling logic for cropping events)&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;fun&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;textBlockEvents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    engine: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Engine,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    block: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; DesignBlock,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    fontFamilyMap: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Map&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;String, FontFamilyData&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;    val&lt;/span&gt;&lt;span&gt; engine &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(engine)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; block &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; fontFamilyMap &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(fontFamilyMap)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // ... (event handling logic for text block events)&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;While the triggering and its API remain unchanged, and no extra parameters need to be passed:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onEvent&lt;/span&gt;&lt;span&gt;(event: &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    eventHandler.&lt;/span&gt;&lt;span&gt;handleEvent&lt;/span&gt;&lt;span&gt;(event)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;final-words&quot;&gt;Final Words&lt;/h2&gt;
&lt;p&gt;As we conclude our journey through Kotlin code refactoring, we’ve unlocked the secrets to enhanced performance and style. By embracing techniques such as HashMaps, infix functions, and inline functions with reified type parameters, we’ve elevated our code to new heights. The benefits are clear: improved efficiency, readability, and adherence to the Single Responsibility Principle. Armed with these tools, you’re now ready to embark on your own coding adventures, transforming messy code into elegant masterpieces.&lt;/p&gt;
&lt;p&gt;If you’d like to try it out, I’ve created a working &lt;a href=&quot;https://pl.kotl.in/tEDorvc04&quot;&gt;example code on the Kotlin Playground&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thank you for accompanying, and happy coding!&lt;/strong&gt; &lt;strong&gt;Never miss out on updates and &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;subscribe&lt;/a&gt; to our newsletter.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Sven</dc:creator><media:content url="https://blog.img.ly/2023/09/hero_01.jpg" medium="image"/><category>How-To</category><category>Android</category><category>Android App Development</category><category>Kotlin</category><category>Insights</category></item><item><title>How to Build a Cross-Platform Editing Experience for Web-to-Print</title><link>https://img.ly/blog/cross-platform-design-editing-for-web-to-print/</link><guid isPermaLink="true">https://img.ly/blog/cross-platform-design-editing-for-web-to-print/</guid><description>Explore the importance of cross-platform web-to-print apps and the challenges of building a unified editing experience.</description><pubDate>Wed, 19 Apr 2023 12:34:14 GMT</pubDate><content:encoded>&lt;p&gt;We have previously talked about the &lt;a href=&quot;https://img.ly/blog/why-mobile-first-is-the-future-of-print/&quot;&gt;importance of taking your web-to-print app cross-platform&lt;/a&gt;. Whether you are operating a print-on-demand platform and you want to retain customers through a mobile app, or you are developing a physical print kiosk at your point of sale, you will not get around a native editing experience.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/embed/cRqm9-c17-c?feature=oembed&quot;&gt;https://www.youtube.com/embed/cRqm9-c17-c?feature=oembed&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Relegating the user experience on mobile to the back seat of your digital strategy or simply using it as a checkout front leaves real money on the table.&lt;/p&gt;
&lt;p&gt;Customers looking for a shopping experience on mobile will not abandon the process in order to switch to the desktop to perform design tasks.&lt;/p&gt;
&lt;p&gt;Unfortunately, it is not as easy as dropping in a web editor into your mobile app or on your website, the editing experience on the web will be massively degraded as compared with a native &lt;a href=&quot;https://img.ly/creative-sdk&quot;&gt;mobile design editor&lt;/a&gt;. Gesture support is limited, and interaction patterns just do not feel native, what is more, operating within the confines of the browser imposes memory constraints and the inability to take advantage of hardware acceleration.&lt;/p&gt;
&lt;p&gt;Fortunately, IMG.LY &lt;a href=&quot;https://img.ly/blog/creative-editor-sdk-cross-platform/&quot;&gt;just released full cross-platform support&lt;/a&gt; of the CreativeEditor SDK. You can now build on IMG.LY’s engine tech on the web, Android and iOS.&lt;/p&gt;
&lt;p&gt;We are going to explore what goes into building a unified editing experience for a particular use case across these platforms while taking into account platform-specific patterns and usage habits.&lt;/p&gt;
&lt;p&gt;But first, we need some background on the technology powering our cross-platform solutions.&lt;/p&gt;
&lt;h2 id=&quot;the-foundation-platform-agnostic-graphical-engine&quot;&gt;The Foundation: Platform Agnostic Graphical Engine&lt;/h2&gt;
&lt;p&gt;The primary challenge of attempting to take a graphics processing SDK cross-platform is avoiding the need to rebuild the same technology on each platform. Instead, we aim to build on previous insights and experience and reuse as much technology as possible. After four years of research and development, IMG.LY’s Creative Engine forms one core piece of software built in C++ that implements the foundational building blocks of state-of-the-art graphics processing. Crucially, the engine can be easily ported to any platform.&lt;/p&gt;
&lt;p&gt;This “build once, port anywhere” approach allows for the rapid rolling out of new platform support while ensuring feature parity and most essentially consistency.&lt;/p&gt;
&lt;h2 id=&quot;ensuring-consistent-cross-platform-output&quot;&gt;Ensuring Consistent Cross-Platform Output&lt;/h2&gt;
&lt;p&gt;Consistency in this context means that design templates can be shared across platforms while always rendering the exact same output. This is challenging to accomplish if your rendering engine differs from platform to platform.&lt;/p&gt;
&lt;p&gt;Something as seemingly innocuous as consistently rendering the same text regardless of platform turns out to be &lt;a href=&quot;https://faultlore.com/blah/text-hates-you/&quot;&gt;notoriously difficult.&lt;/a&gt;&lt;br&gt;
In the context of print applications, this is the biggest technical hurdle to clear.&lt;/p&gt;
&lt;p&gt;It is imperative for designs on mobile and the web to produce the same print-ready output file, such as a PDF. Furthermore, to be effective across platforms, users expect to save work in progress and resume it on another device. Therefore, we must support the ability to serialize and deserialize in-progress designs while ensuring consistency.&lt;/p&gt;
&lt;p&gt;In addition to interoperability during the design process, it is often necessary to re-render user-generated designs. This is the case when designs are intended to be applied to much larger media than can be rendered on a device, such as high-resolution banners or posters. In this scenario, the serialized edits must be applied to the high-resolution medium on a server, adding yet another platform into the mix.&lt;/p&gt;
&lt;h2 id=&quot;designing-uis---balancing-requirements-and-constraints&quot;&gt;Designing UIs - Balancing Requirements and Constraints&lt;/h2&gt;
&lt;p&gt;To illustrate the various platform requirements and constraints involved in designing UIs, we developed starter kit editors using the CE.SDK engine for apparel design. Our &lt;a href=&quot;https://img.ly/showcases/cesdk/web/apparel-ui/web/index.html&quot;&gt;apparel designer&lt;/a&gt; enables users to design the front of a t-shirt for deployment on web storefronts, mobile apps (&lt;a href=&quot;https://play.google.com/store/apps/details?id=ly.img.cesdk.catalog&quot;&gt;Android&lt;/a&gt; and &lt;a href=&quot;https://apps.apple.com/app/img-ly-design-editor/id1672991141&quot;&gt;iOS&lt;/a&gt;), and print kiosks for in-store customization.&lt;/p&gt;
&lt;p&gt;While we could have used our &lt;a href=&quot;https://img.ly/showcases/cesdk/web/&quot;&gt;default Design UI&lt;/a&gt; and added a t-shirt backdrop, we opted for a more specific approach that prioritizes the features necessary for an optimal t-shirt design experience. Our UI places these features front and center, while also providing guardrails to prevent bad designs and hide unnecessary complexity.&lt;/p&gt;
&lt;p&gt;Thankfully, the CE.SDK Engine allows for the creation of entirely custom UIs using any UI framework, thanks to its image and video manipulation API.&lt;/p&gt;
&lt;p&gt;Our &lt;a href=&quot;https://img.ly/showcases/cesdk/web/apparel-ui&quot;&gt;custom UI for editing t-shirts&lt;/a&gt; on the web can be used on both desktop and mobile web, as well as deployed to a print kiosk inside a web view. To create an optimal user experience, we identified the most important editing options for a t-shirt design in order of relevance: overlaying text, adding images, shapes, and stickers, and uploading new image assets.&lt;/p&gt;
&lt;p&gt;It’s worth noting that users start from a template rather than a blank canvas. We strive to give users the best starting point for their creative journey, minimizing setup and menial work as much as possible. The same principle applies to discovering editing features. When a user selects an element, a context menu appears with essential editing options. For example, for a text element, options would include changing color, font, or alignment.&lt;/p&gt;
&lt;h2 id=&quot;template-creation-on-the-web&quot;&gt;Template Creation on the Web&lt;/h2&gt;
&lt;p&gt;The most important difference between the web and mobile use cases is that templates will in almost all cases be created using the web interface, whereas users on mobile will simply adapt existing templates.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/embed/VLBXMxQMKak?feature=oembed&quot;&gt;https://www.youtube.com/embed/VLBXMxQMKak?feature=oembed&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This means that in addition to the user-facing apparel UI, we also need to ship a more complex UI allowing the creation of templates. Creating templates requires more granular control over an element’s design, as well as the ability to define &lt;a href=&quot;https://img.ly/showcases/cesdk/web/placeholders/web/index.html&quot;&gt;placeholders and design constraints&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These templates can then be deployed to the simpler adoption UIs on mobile, where the users’ design is guided by those constraints.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Publish your templates to simple user interfaces for your users.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1587px) 1587px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1587&quot; height=&quot;1007&quot; src=&quot;https://img.ly/_astro/cross-platform-editor-SDK-1_Z1nLEks.webp&quot; srcset=&quot;/_astro/cross-platform-editor-SDK-1_1XAKHl.webp 640w, /_astro/cross-platform-editor-SDK-1_Z2w10yM.webp 750w, /_astro/cross-platform-editor-SDK-1_ZsVg61.webp 828w, /_astro/cross-platform-editor-SDK-1_ZN7wUQ.webp 1080w, /_astro/cross-platform-editor-SDK-1_GyTCX.webp 1280w, /_astro/cross-platform-editor-SDK-1_Z1nLEks.webp 1587w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;mobile-uis&quot;&gt;Mobile UIs&lt;/h2&gt;
&lt;p&gt;Designing for mobile devices presents a unique set of challenges due to the limited screen size, reliance on gestures for interaction, and hardware and bandwidth constraints. To address these constraints, we take a strict template-first approach. Most users prefer to begin their work from a template or continue working on a design they started on their desktop. Thus, the first step in the user journey is browsing and selecting designs.&lt;/p&gt;
&lt;p&gt;In addition, we need to consider mobile interaction patterns and be even more context-sensitive than we are on the web. For instance, in a print context, the asset size is typically larger than the device’s screen size. This size limitation affects the navigation pattern, and users need to zoom in by pinching to focus on smaller elements. However, we cannot use the pinch gesture to modify an element’s size because it conflicts with the zoom gesture. Instead, users must first select the element explicitly to activate it and then change its size via a pinch gesture.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Pinch to zoom, then tap to select and edit objects, stress-free.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 190px) 190px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;190&quot; height=&quot;400&quot; src=&quot;https://img.ly/_astro/zoom-and-tap-to-select-Creative-Editor_SDK--min_Z1vrQTR.webp&quot; srcset=&quot;/_astro/zoom-and-tap-to-select-Creative-Editor_SDK--min_Z1vrQTR.webp 190w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Creating user interfaces for designing &lt;a href=&quot;https://img.ly/industries/print&quot;&gt;print products&lt;/a&gt; on the web and mobile that allow users to be productive is a complex task. We started out by considering the technical complexities involved in bringing any cross-platform editing solution to fruition, and went on to consider both the broad strokes workflow and the minute details of individual element manipulation.&lt;/p&gt;
&lt;p&gt;You can adapt the cross-platform apparel editor that we built in this process and integrate it into your product. Hopefully, these example UIs form a useful foundation on which to build your use case.&lt;/p&gt;
&lt;p&gt;Designing user interfaces that allow users to create print products on web and mobile platforms is a complex task that requires a thorough understanding of technical and UX complexities. Our team embarked on this journey with a focus on bringing a cross-platform editing solution to life. We delved into the intricate details of individual element manipulation and considered the broad strokes of template-based workflow design, so you don’t have to.&lt;/p&gt;
&lt;p&gt;Through this process, we developed a &lt;a href=&quot;https://img.ly/use-cases/apparel&quot;&gt;cross-platform apparel editor&lt;/a&gt; that can be easily adapted and integrated into your product. Our aim is to provide you with a solid foundation that will enable you to build a UI that is tailored to your use case. With our &lt;a href=&quot;https://img.ly/showcases/cesdk/web/&quot;&gt;example UIs&lt;/a&gt; as a starting point, you can develop a product that empowers your users to create and customize print products with ease.&lt;/p&gt;
&lt;p&gt;In conclusion, designing a user interface that allows for print product creation on web and mobile platforms is a complex undertaking. However, with the right approach and attention to detail, it is possible to create a UI that is both intuitive and efficient. Our team has provided a starting point with our cross-platform apparel editor, and we hope that it serves as a useful foundation for your own UI development efforts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://img.ly/forms/contact-sales&quot;&gt;Reach out to our solutions team&lt;/a&gt; to explore your particular use case.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Jan</dc:creator><media:content url="https://blog.img.ly/2023/04/build-cross_platform-print-edior-web_to_print.jpg" medium="image"/><category>How-To</category><category>Tech</category><category>Web-to-print</category><category>Print</category><category>Learning</category></item><item><title>How Printers Can Leverage Creative Automation</title><link>https://img.ly/blog/how-printers-can-leverage-creative-automation/</link><guid isPermaLink="true">https://img.ly/blog/how-printers-can-leverage-creative-automation/</guid><description>Discover how creative automation can revolutionize print production and transform the print industry.</description><pubDate>Tue, 04 Apr 2023 09:58:54 GMT</pubDate><content:encoded>&lt;p&gt;The emergence of print as a service and print-on-demand has changed the traditional way we think about printing, creating many possibilities for customized products, such as personalized photo books and bespoke t-shirts. However, to meet the ever-evolving needs of customers, printers must constantly innovate and streamline their operations. This is where &lt;strong&gt;creative automation&lt;/strong&gt; wins.&lt;/p&gt;
&lt;p&gt;According to &lt;a href=&quot;https://beta3.infotrends.com/news/editors-desk/2020/august/how-to-leverage-automation-for-print-production-in-hard-times/&quot;&gt;Keypoint Intelligence&lt;/a&gt;, only a mere 7% of Print Service Providers in North America had adopted a fully or mostly automated approach across their departments in 2020. This underscores the crucial role that creative automation can play in transforming the print industry. But what exactly does creative automation mean, and how can it revolutionize print production? Let’s find out!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1200px) 1200px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1200&quot; height=&quot;742&quot; src=&quot;https://img.ly/_astro/automation-statistics_1KcImB.webp&quot; srcset=&quot;/_astro/automation-statistics_Z2sVV8u.webp 640w, /_astro/automation-statistics_Z1x4snL.webp 750w, /_astro/automation-statistics_Z1rFMM3.webp 828w, /_astro/automation-statistics_365jk.webp 1080w, /_astro/automation-statistics_1KcImB.webp 1200w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-is-creative-automation&quot;&gt;What is Creative Automation?&lt;/h2&gt;
&lt;p&gt;To understand the power of creative automation, it’s critical to draw a line between the &lt;strong&gt;production of creative assets&lt;/strong&gt; and &lt;strong&gt;ad units&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Although many creative assets may eventually turn into ads, they first need to be integrated into a broader ad unit that incorporates other non-graphical elements like &lt;em&gt;headlines, text&lt;/em&gt;, and &lt;em&gt;landing pages&lt;/em&gt;. This ad unit is then assigned to a specific campaign.&lt;/p&gt;
&lt;p&gt;By leveraging creative automation, each stage of the creative asset production process can be automated, enabling the entire ad production process across all placements to be streamlined.&lt;/p&gt;
&lt;p&gt;To clarify, dynamic creative automates media buying, while creative automation utilizes advanced software to produce stunning designs quickly. This approach offers several benefits, including improved efficiency, reduced costs, and higher product quality.&lt;/p&gt;
&lt;h2 id=&quot;creative-automation-for-print-service-providers-and-print-on-demand&quot;&gt;Creative Automation for Print Service Providers and Print-on-Demand&lt;/h2&gt;
&lt;p&gt;The potential advantages of creative automation for &lt;strong&gt;Print Service Providers&lt;/strong&gt; are significant, particularly in terms of streamlining the design process.&lt;/p&gt;
&lt;p&gt;With tools like CE.SDK, printers can rapidly create bespoke designs for t-shirts, business cards, and other products, while also taking advantage of a wide range of templates, styles, and layouts that can be easily customized to suit any project. This not only saves time, but also ensures that each design is unique and of outstanding quality.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Print-on-Demand companies&lt;/strong&gt;, on the other hand, can also leverage the power of creative automation to offer personalized products to customers in a rapid and efficient manner.&lt;/p&gt;
&lt;p&gt;By utilizing creative automation tools, print-on-demand companies can develop high-quality designs for photo books, t-shirts, and mugs, without the need for manual intervention in the design process.  This evolves into significant time savings, cost reductions, and increased profitability, allowing print-on-demand companies to stay ahead of the competition.&lt;/p&gt;
&lt;h2 id=&quot;how-to-apply-creative-automation-in-print&quot;&gt;How to Apply Creative Automation in Print&lt;/h2&gt;
&lt;p&gt;So, how can printers apply creative automation in print? There are several key elements to consider, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Variable Data Printing&lt;/strong&gt; is a printing technique that allows the personalization of each print piece, based on specific customer data. By automating this process, Print Service Providers can increase efficiency and accuracy, while reducing costs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Constraints&lt;/strong&gt; might seem like a limitation, but they’re actually a key factor in ensuring that designs are consistent during the creation process and meet industry standards. By automating the constraint process, printers can ensure that designs are consistent and meet expectations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Templates&lt;/strong&gt; are pre-designed layouts that can be used for specific products, such as business cards or brochures. Automation of the template process allows the quickly creating of high-quality designs, without having to start from scratch each time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Styles&lt;/strong&gt; are a set of design elements, such as colors, fonts, and images, that can be used to create a consistent look and feel across all designs. The style automation process brings the possibility to create a cohesive brand image, increasing brand recognition and customer loyalty.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Layouts&lt;/strong&gt; refer to the arrangement of design elements on a page, such as images, text, and graphics. Their automation provides fast creation of stunning designs, without having to manually arrange each element.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Design Validation&lt;/strong&gt; is like having an eagle eye that spots mistakes (such as incorrect dimensions) before they become costly printing errors. Automating the design validation process allows printers to quickly identify and correct errors, reducing the risk of printing errors and increasing customer satisfaction.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;creative-automation-in-print-with-cesdk&quot;&gt;Creative Automation in Print with CE.SDK&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://img.ly/creative-sdk&quot;&gt;&lt;strong&gt;CE.SDK&lt;/strong&gt;&lt;/a&gt; is a powerful tool for creative automation in print, taking care of the entire design process, from layout to validation.&lt;/p&gt;
&lt;p&gt;With CE.SDK, you can easily create brilliant designs for various products, without having to spend time on manual design processes. The platform also offers a range of templates, styles, and layouts that can be easily customized to meet the needs of each project.&lt;/p&gt;
&lt;p&gt;For instance, let’s say you want to create a gorgeous photo book for a client. With CE.SDK, all you have to do is upload your images, select a template, and customize the design elements.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Let your users cherish their favorite memories by uploading their photos to a photo book template. Try this custom UI yourself in our showcases.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1395px) 1395px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1395&quot; height=&quot;865&quot; src=&quot;https://img.ly/_astro/image-0-min_Z28lC66.webp&quot; srcset=&quot;/_astro/image-0-min_rOyS8.webp 640w, /_astro/image-0-min_24VFzY.webp 750w, /_astro/image-0-min_Z26BOug.webp 828w, /_astro/image-0-min_Z1KICQ3.webp 1080w, /_astro/image-0-min_ZwC5Hb.webp 1280w, /_astro/image-0-min_Z28lC66.webp 1395w&quot;&gt;&lt;/p&gt;
&lt;p&gt;CE.SDK will take care of the rest, automatically arranging the images into a beautifully designed photo book that includes text, graphics, and other design elements. This not only saves time, but also ensures that each photo book is unique and of high quality.&lt;/p&gt;
&lt;p&gt;Here are some easy steps to follow when creating a &lt;a href=&quot;https://img.ly/showcases/cesdk/web/photo-book-ui/index.html&quot;&gt;photo book&lt;/a&gt;:&lt;/p&gt;
&lt;h4 id=&quot;step-1-organize-your-photos&quot;&gt;Step 1: Organize your Photos&lt;/h4&gt;
&lt;p&gt;Choose the photos you want to include in your photo book and organize them in a way that tells a story. For instance, you can arrange them chronologically or by theme.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Storytelling is everything – allow users to organize their photos to pick and choose from.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1351px) 1351px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1351&quot; height=&quot;810&quot; src=&quot;https://img.ly/_astro/photo-book-SDK-template-for-web_1t67rX.webp&quot; srcset=&quot;/_astro/photo-book-SDK-template-for-web_MdgNn.webp 640w, /_astro/photo-book-SDK-template-for-web_Z2dbxzd.webp 750w, /_astro/photo-book-SDK-template-for-web_Z1Rj0aW.webp 828w, /_astro/photo-book-SDK-template-for-web_Z1eahNS.webp 1080w, /_astro/photo-book-SDK-template-for-web_Z1FNhkJ.webp 1280w, /_astro/photo-book-SDK-template-for-web_1t67rX.webp 1351w&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;step-2-customize-your-photo-book&quot;&gt;Step 2: Customize your Photo Book&lt;/h4&gt;
&lt;p&gt;Then, add a personal touch to your photo book by customizing the text, adding captions, or changing the font style and color.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Users might expect custom text options to give their memories a final touch.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1007px) 1007px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1007&quot; height=&quot;109&quot; src=&quot;https://img.ly/_astro/photo-book-SDK-template-for-web_2_NADSD.webp&quot; srcset=&quot;/_astro/photo-book-SDK-template-for-web_2_ZpDR6j.webp 640w, /_astro/photo-book-SDK-template-for-web_2_ZXVJkj.webp 750w, /_astro/photo-book-SDK-template-for-web_2_K4mom.webp 828w, /_astro/photo-book-SDK-template-for-web_2_NADSD.webp 1007w&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;step-3&quot;&gt;Step 3&lt;/h4&gt;
&lt;p&gt;Once you’ve finished customizing your template, you can preview the final result, and send it out for printing.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;A memory ready for print!&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1066px) 1066px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1066&quot; height=&quot;755&quot; src=&quot;https://img.ly/_astro/photo-book-SDK-template-for-web_3_ZnPlWR.webp&quot; srcset=&quot;/_astro/photo-book-SDK-template-for-web_3_Z15cjv0.webp 640w, /_astro/photo-book-SDK-template-for-web_3_Z155fNY.webp 750w, /_astro/photo-book-SDK-template-for-web_3_Z132YqD.webp 828w, /_astro/photo-book-SDK-template-for-web_3_ZnPlWR.webp 1066w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In today’s ever-changing printing industry, printers need to be efficient, innovative, and creative to remain competitive. Creative automation provides an excellent solution, enabling printers to streamline their design process, reduce costs, and improve profitability while delivering exceptional, personalized products to their customers.&lt;/p&gt;
&lt;p&gt;CE.SDK is a powerful tool for creative automation in print, offering a wide range of customizable templates, styles, and layouts that can be tailored to meet the unique requirements of each project. With CE.SDK, printers can create stunning designs for a range of products, from personalized t-shirts to exquisite photo books, all while saving time and money.  &lt;a href=&quot;https://img.ly/free-trial&quot;&gt;Try CE.SDK for free&lt;/a&gt; and see the benefits of creative automation for yourself.&lt;/p&gt;</content:encoded><dc:creator>Natalia</dc:creator><media:content url="https://blog.img.ly/2023/04/design_automation_SDK-min.jpg" medium="image"/><category>How-To</category><category>Automation</category><category>Print</category><category>Creative Editor</category><category>Learning</category></item><item><title>How to Build a TikTok Clone for iOS with Swift &amp; CreativeEditor SDK</title><link>https://img.ly/blog/how-to-build-a-tiktok-clone-for-ios/</link><guid isPermaLink="true">https://img.ly/blog/how-to-build-a-tiktok-clone-for-ios/</guid><description>Learn how to build a TikTok clone for iOS with Swift and CreativeEditor SDK with this step-by-step in-depth tutorial.</description><pubDate>Thu, 30 Mar 2023 11:20:26 GMT</pubDate><content:encoded>&lt;p&gt;Now that TikTok is facing a ban in the US any day now, we better wait in the wings with an alternative ready to go and scoop up those millions of hobby dancers, micro bloggers, and would-be influencers.&lt;/p&gt;
&lt;p&gt;In this article, you’ll learn how to use Swift, SwiftUI, and the IMG.LY CreativeEditor SDK to build a simple iOS app for recording, editing, and viewing short videos. This app features views providing core functionalities similar to making a post in a video-sharing platform like TikTok.&lt;/p&gt;
&lt;p&gt;The editing controller is the central hub. It allows users to capture video clips and enhance their videos by adding filters, stickers, music, and other effects. CreativeEditor SDK calls the overall project a “scene” and the parts (clips, stickers, text, etc.) “blocks”. Once the scene is ready, the user can compose it into a standard video file and export it. Finally, the playback controller showcases the finished projects using Apple’s standard VideoPlayer struct. For recording and editing, the CreativeEditor SDK’s Camera and Video Editor handle the heavy lifting, enabling swift development of a robust app. You can easily extend its features, filters, and creative options as your users demand.&lt;/p&gt;
&lt;p&gt;Follow along to see how to capture video, enhance it, and export the finished product as a polished movie file.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-your-project&quot;&gt;Setting Up Your Project&lt;/h2&gt;
&lt;p&gt;You can try out the TikTok clone by cloning the &lt;a href=&quot;https://github.com/imgly/tiktok-clone-ios-cesdk&quot;&gt;GitHub repository supporting the article&lt;/a&gt;. Otherwise, follow this step-by-step tutorial and learn how to build the TikTok clone with CreativeEditor SDK by yourself.&lt;/p&gt;
&lt;p&gt;To add the CreativeEditor SDK to an Xcode project, include it with your other package dependencies. Use the URL below to add the package, either to your &lt;code&gt;Package.swift&lt;/code&gt; file or using &lt;code&gt;File &gt; Add Packages...&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;https://github.com/imgly/IMGLYUI-swift&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The package includes a number of different frameworks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;IMGLYUI, an umbrella framework of all of the different editors and a camera.&lt;/li&gt;
&lt;li&gt;IMGLYCamera, a standalone camera View. This is the same camera as the editors use.&lt;/li&gt;
&lt;li&gt;IMGLYApparelEditor, IMGLYDesignEditor, IMGLYPostcardEditor, and IMGLYVideoEditor which are four different configurations to support different use cases.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these frameworks uses the IMG.LY Engine for image processing. To include them in your project, navigate to the ‘Frameworks, Libraries, and Embedded Content’ section in your app’s General settings. Click the ’+’ button and select the desired frameworks. Initially, consider adding the IMGLYUI package, as it encompasses all other packages. Before releasing your app, review and include only the necessary components to minimize app size and avoid unused code.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Adding a framework to the app&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 590px) 590px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;590&quot; height=&quot;646&quot; src=&quot;https://img.ly/_astro/frameworks_ZpiIEt.webp&quot; srcset=&quot;/_astro/frameworks_ZpiIEt.webp 590w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once Xcode downloads and resolves the packages, you just need to &lt;code&gt;include IMGLYVideoEditor&lt;/code&gt; in any of the files that will reference the editor in your app. When you’re working with the underlying engine directly you’ll also want to &lt;code&gt;include IMGLYEngine&lt;/code&gt;. You’ll only work with the engine as you start to customize the workflow.&lt;/p&gt;
&lt;h2 id=&quot;asking-for-permissions&quot;&gt;Asking for Permissions&lt;/h2&gt;
&lt;p&gt;Before any app can access the phone’s microphone, camera, documents or photo library, the user must specifically give permission to record audio or video as well as access the user’s Photo library. If your app doesn’t secure these permissions properly before trying to use the camera the app will crash and also probably won’t get through Apple’s app review. In earlier versions of iOS you could develop an app on the Simulator without asking permissions, but always on the device the app will crash. In current versions of iOS the app crashes regardless of platform. The dialogue requesting permissions is a system dialog and looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;permission dialog&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 820px) 820px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;820&quot; height=&quot;453&quot; src=&quot;https://img.ly/_astro/permission1_ZWFkBh.webp&quot; srcset=&quot;/_astro/permission1_1LPnuv.webp 640w, /_astro/permission1_OFysD.webp 750w, /_astro/permission1_ZWFkBh.webp 820w&quot;&gt;&lt;/p&gt;
&lt;p&gt;You do not have the ability to change this dialog. You can supply the reason you are asking for the permission using a key in the &lt;code&gt;info.plist&lt;/code&gt; file in the Xcode project. In the example above “Lets you record videos” is in the &lt;code&gt;info.plist&lt;/code&gt;. For the video camera you will have to ask for video and audio permission. The first step is to add a short message to the user in the &lt;code&gt;info.plist&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Info plist example&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 723px) 723px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;723&quot; height=&quot;102&quot; src=&quot;https://img.ly/_astro/infoplist_FispA.webp&quot; srcset=&quot;/_astro/infoplist_1Jamxn.webp 640w, /_astro/infoplist_FispA.webp 723w&quot;&gt;&lt;/p&gt;
&lt;p&gt;For video the key to add is &lt;code&gt;NSCameraUsageDescription&lt;/code&gt; and for the microphone you need to add &lt;code&gt;NSMicrophoneUsageDescription&lt;/code&gt;. Whenever your app attempts to use the camera, iOS will first check to see if the user has already granted access. If not, it will then display the dialogs using your entries in the &lt;code&gt;info.plist&lt;/code&gt; or crash if you have not provided entries. The user might be surprised by these dialog boxes and may accidentally tap &lt;code&gt;Don&apos;t Allow&lt;/code&gt; if they are trying to quickly launch the camera. It is better practice to set up some kind of onboarding view and secure the permissions before you need them. You might have a view that explains what you are going to ask for and then displays the permission dialog.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt; AVCaptureDevice.&lt;/span&gt;&lt;span&gt;authorizationStatus&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt;: .video) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  case&lt;/span&gt;&lt;span&gt; .authorized&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //the user has authroized permission!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    break&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  case&lt;/span&gt;&lt;span&gt; .denied&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //the user has previously denied permission!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //perhaps we should ask them again&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    break&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  case&lt;/span&gt;&lt;span&gt; .restricted&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //the user cannot grant permission!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    break&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  case&lt;/span&gt;&lt;span&gt; .notDetermined&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //the user has never been asked for permission&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //so let&apos;s ask them now&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    AVCaptureDevice.&lt;/span&gt;&lt;span&gt;requestAccess&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt;: .video) { accessGranted &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      if&lt;/span&gt;&lt;span&gt; accessGranted {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        //the user has authorized permission!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        //the user has denied permission!&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;  @unknown&lt;/span&gt;&lt;span&gt; default:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    break&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The snippet above lets your app read the permission status for the video camera and ask for permission if it has not been granted. To request access to the microphone pass &lt;code&gt;.audio&lt;/code&gt; to the &lt;code&gt;AVCaptureDevice.authorizationStatus(for:)&lt;/code&gt; and &lt;code&gt;AVCaptureDevice.requestAccess(for:)&lt;/code&gt; functions. The &lt;code&gt;AVCaptureDevice.requestAccess(for:)&lt;/code&gt; command is what displays the system dialog actually requesting access. The &lt;code&gt;accessGranted&lt;/code&gt; variable reports back to your app what the user chose. You can take care of getting the permissions any time but when the CreativeEditor SDK first launches it will attempt to access the camera, so the dialog will appear if the user has not already granted permission.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;.restricted&lt;/code&gt; case is fairly new. In this case, there are policies on the phone that prohibit the user from granting permission to your app. In addition to asking permissions during onboarding, it is good practice to check for permission every time before you attempt to run any code that uses the camera. The user can change permissions using the Settings app on their phone at any time. If the user has denied permissions and you present the video camera anyway, your app will record black video frames and silence on audio tracks.&lt;/p&gt;
&lt;p&gt;In addition to asking for camera and microphone permissions, your app will probably want to access the photos on the user’s phone. You will need to add an entry to the &lt;code&gt;info.plist&lt;/code&gt; for &lt;code&gt;NSPhotoLibraryUsageDescription&lt;/code&gt;. As with the camera and the microphone, the dialog will appear when the Video Editor first attempts access, but you can give your user some ability to grant permission during onboarding. For the user’s photos, you can check the authorization status using&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; status &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; PHPhotoLibrary.&lt;/span&gt;&lt;span&gt;authroizationStatus&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt;: .readWrite)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As with the camera, the photo library has &lt;code&gt;PHPhotoLibrary.requestAuthroization(for: .readWrite)&lt;/code&gt; but instead of just returning a “granted” or “denied” status, this command returns the actual new status. In addition to the status values for the camera, the photo library may return a &lt;code&gt;.limited&lt;/code&gt; status meaning that the user has granted permission to only some of the photos. If the user has chosen to share only some of their photos, Apple provides some views you can present so that the user can manage the photos. Any videos that your app saves to the user’s photo library will always be available when the user has chosen &lt;code&gt;.limited&lt;/code&gt;. You can read more about how to work with permissions and the user’s photo library by reading this Apple article &lt;a href=&quot;https://developer.apple.com/documentation/photokit/delivering-an-enhanced-privacy-experience-in-your-photos-app&quot;&gt;Delivering an Enhanced Privacy Experience in Your Photos App&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;making-video-recordings&quot;&gt;Making Video Recordings&lt;/h2&gt;
&lt;p&gt;The start of a great TikTok is the camera. When our user wants to make a new video clip with our app, they tap on the button at the bottom of the initial screen to start the creation workflow.&lt;/p&gt;
&lt;p&gt;Here there is a decision to make. TikTok and the CreativeEditor SDK can each start with the creation controls and a video preview. When using the CreativeEditor SDK though, you can also start with some video that came from somewhere else. So if you already have some camera code you like, or if you want to start with some video clip from somewhere else, you can insert that into the editor when it launches. You can do that by leveraging the &lt;code&gt;onCreate&lt;/code&gt; modifier of the editor.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; engine &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; EngineSettings&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;license&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&amp;#x3C;your license code&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                              userID&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&amp;#x3C;your unique user id&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;VideoEditor&lt;/span&gt;&lt;span&gt;(engine)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  .imgly.&lt;/span&gt;&lt;span&gt;onCreate&lt;/span&gt;&lt;span&gt;({ engine &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    try&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; engine.scene.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fromVideo&lt;/span&gt;&lt;span&gt;: Bundle.main.&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;forResource&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;dog_water&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;withExtension&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;mov&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //set other defaults&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;In the code above, the app initializes an instance of the Video Editor view and then reads the &lt;code&gt;dog_water.mov&lt;/code&gt; file from the app bundle. launches the editor with that.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Starting scene with a dog video&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 480px) 480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;480&quot; height=&quot;1039&quot; src=&quot;https://img.ly/_astro/prepopulated_2uzsKx.webp&quot; srcset=&quot;/_astro/prepopulated_2uzsKx.webp 480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Whether starting with a video or starting with a blank canvas a user can add video clips to their creation using the embedded camera.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;difference between tiktok camera and creativeeditor&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 640px) 640px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;640&quot; height=&quot;674&quot; src=&quot;https://img.ly/_astro/updated_editor_compare_1tFFER.webp&quot; srcset=&quot;/_astro/updated_editor_compare_1tFFER.webp 640w&quot;&gt;&lt;/p&gt;
&lt;p&gt;The image above compares two different camera controllers: the left one from the TikTok app and the right showing the default settings of the MobileCamera, either standalone or within a Video Editor scene. Key controls in each are marked with blue numbers:&lt;/p&gt;
&lt;p&gt;1 - Flip camera button&lt;br&gt;
2 - Flash button&lt;br&gt;
3 - Recording time indicator&lt;br&gt;
4 - Add video and finish buttons&lt;br&gt;
5 - Cancel button&lt;/p&gt;
&lt;p&gt;While the TikTok camera integrates several editing functions during video capture, the Video Editor separates these tasks, focusing the camera purely on recording. Both systems allow users to start and stop video recording to compile a series of clips before moving to the editing phase.&lt;/p&gt;
&lt;h3 id=&quot;configuring-the-camera&quot;&gt;Configuring the Camera&lt;/h3&gt;
&lt;p&gt;The camera used inside of the Video Editor UI isn’t customizable. You have limited options to customize when you use the standalone camera though. You can set the colors of the buttons, helpful if you have a preferred color scheme, and you can set a maximum duration for the video recording allowed.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; config &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CameraConfiguration&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  recordingColor&lt;/span&gt;&lt;span&gt;: .orange,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  highlightColor&lt;/span&gt;&lt;span&gt;: .blue,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  maxTotalDuration&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;30&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  allowExceedingMaxDuration&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;Camera&lt;/span&gt;&lt;span&gt;(engine, &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;: config) { result &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; //handle the recorded video collection&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code above sets the record button to orange while recording and the clip view to orange. There is a maximum duration of 30 seconds for all videos in the collection and the button to save the clips highlighted in blue.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;customized camera&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 480px) 480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;480&quot; height=&quot;1039&quot; src=&quot;https://img.ly/_astro/customcamera_Z2tXDPj.webp&quot; srcset=&quot;/_astro/customcamera_Z2tXDPj.webp 480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;If your app requires further customization, you’ll probably want to make your own camera view and work with the IMG.LY Creative Engine directly.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://img.ly/docs/cesdk/ios/get-started/overview-e18f40/&quot;&gt;documentation website&lt;/a&gt; is a great resource for understanding how much you can add to and customize the IMGLYUI resources and when you’ll need to drop down to the level of the engine.&lt;/p&gt;
&lt;h3 id=&quot;presenting-the-controller&quot;&gt;Presenting the Controller&lt;/h3&gt;
&lt;p&gt;As demonstrated above, both the Video Editor and the Camera are View structures that can be presented directly or through overlays such as overlay or fullScreenCover. However, they are typically enclosed in a NavigationView to utilize the navigation bar for displaying export and other buttons. Therefore, when triggering the Video Editor from a button, it’s crucial to embed it within a NavigationView.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Edit the Video&quot;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  isPresented &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; .&lt;/span&gt;&lt;span&gt;fullScreenCover&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;isPresented&lt;/span&gt;&lt;span&gt;: $isPresented) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   NavigationView&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     VideoEditor&lt;/span&gt;&lt;span&gt;(engine)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      //any setup modifiers&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;building-your-creation&quot;&gt;Building Your Creation&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;compare editors&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 640px) 640px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;640&quot; height=&quot;674&quot; src=&quot;https://img.ly/_astro/updated_editor_compare-1_ZncnjS.webp&quot; srcset=&quot;/_astro/updated_editor_compare-1_ZncnjS.webp 640w&quot;&gt;&lt;/p&gt;
&lt;p&gt;After capturing a video, the app displays an editor to complete the creation. The editing tools are in the red circles. TikTok provides about ten different tools on the editing screen. Additionally, TikTok provided some editing tools on the capture screen. The CreativeEditor SDK apps provide a scrolling set of asset libraries along the bottom for items to add to the scene. But when any of the elements of the scene, like your video or an audio clip, are highlighted, the bottom row becomes tools to manipulate that element. The basic Asset Library types are uploads, videos, audio, images, text, shapes, and stickers.&lt;/p&gt;
&lt;p&gt;How to configure and customize each of these tools is beyond the scope of this tutorial. But, a lot of the customization of the assets, fonts, blurs and even filters can be done without code. By design, the CreativeEditor SDK downloads assets from a central server. This is a great way for you to be able to push updated filters, stickers, and other assets to your users without going through the app update process. In the next section, we’ll walk through that and provide some links for further research.&lt;/p&gt;
&lt;h3 id=&quot;configuring-the-editors-assets&quot;&gt;Configuring the Editors Assets&lt;/h3&gt;
&lt;p&gt;The IMG.LY Engine looks for assets for stickers, vector shapes, LUT filters, color filters, color palettes, blurs, and fonts using a URL by default. The design of the system is that you would have your own server supporting your app. However, assets in the app bundle are also accessible by URL, so as long as the assets are in the folder format that the engine expects, it doesn’t matter whether they are local or remote.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://img.ly/docs/cesdk/ios/serve-assets-b0827c/&quot;&gt;documentation for this process&lt;/a&gt; you can find a URL to the default asset bundle. Once you download that bundle you can add it to your app. The bundle is structured much like a regular iOS Assets.catalog with some JSON containing metadata about the asset and a file that provides the actual asset. For some of the assets like filters and vectors, no file is needed as everything can be defined in the JSON.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;asset files and metadata listing&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 700px) 700px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;700&quot; height=&quot;656&quot; src=&quot;https://img.ly/_astro/assets_ZLXreD.webp&quot; srcset=&quot;/_astro/assets_21dhby.webp 640w, /_astro/assets_ZLXreD.webp 700w&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the image above, you can see the JSON to describe the ape sticker and the &lt;code&gt;emoji_ape&lt;/code&gt; image file after the IMGLYAssets bundle has been added to our app. You can edit these assets and add your own.&lt;/p&gt;
&lt;p&gt;Once you’re done editing the asset bundle, you can use it instead of the default bundle during the &lt;code&gt;onCreate&lt;/code&gt; modifier like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;VideoEditor&lt;/span&gt;&lt;span&gt;(engine)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  .imgly.&lt;/span&gt;&lt;span&gt;onCreate&lt;/span&gt;&lt;span&gt;({ engine &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     try&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; engine.scene.&lt;/span&gt;&lt;span&gt;load&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;: VideoEditor.defaultScene)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              // Add asset sources&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     let&lt;/span&gt;&lt;span&gt; bundleURL &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Bundle.main.&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;forResource&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;IMGLYAssets&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;withExtension&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;bundle&quot;&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;     try&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; engine.&lt;/span&gt;&lt;span&gt;addDefaultAssetSources&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;baseURL&lt;/span&gt;&lt;span&gt;: bundleURL)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     try&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; engine.&lt;/span&gt;&lt;span&gt;addDemoAssetSources&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sceneMode&lt;/span&gt;&lt;span&gt;: engine.scene.&lt;/span&gt;&lt;span&gt;getMode&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       withUploadAssetSources&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;     try&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; engine.asset.&lt;/span&gt;&lt;span&gt;addSource&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;TextAssetSource&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;engine&lt;/span&gt;&lt;span&gt;: engine))&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;In the code above, we create a blank &lt;code&gt;scene&lt;/code&gt; using &lt;code&gt;VideoEditor.defaultScene&lt;/code&gt; static method. Then set a URL to point to the bundle in the app instead of a remote bundle and make that the source of &lt;code&gt;addDefaultAssetSources&lt;/code&gt;. Now our app will use the assets from the bundle.&lt;/p&gt;
&lt;h3 id=&quot;exporting-the-finished-video&quot;&gt;Exporting the Finished Video&lt;/h3&gt;
&lt;p&gt;Once the user has finished with their creation, they tap the share button and the Video Editor composes all of the video, audio, filters, and static items into a single &lt;code&gt;.mp4&lt;/code&gt; file. Then it displays a share sheet. If you’d rather do something different, like save the creation locally so you can play it back, you can override this default behavior.&lt;/p&gt;
&lt;p&gt;In your app, after the &lt;code&gt;.onCreate&lt;/code&gt; modifier, you can define a &lt;code&gt;.onExport&lt;/code&gt; modifier. When the user taps on the share button you can export the video and then save it to the documents directory to playback later.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://img.ly/docs/cesdk/ios/user-interface/events-514b70/&quot;&gt;documentation for modifiers&lt;/a&gt; you can find the source code for the Video Editor’s default behavior. Instead of duplicating it in detail here, we can just summarize and provide a little snippet for our change.&lt;/p&gt;
&lt;p&gt;First, the &lt;code&gt;.onCreate&lt;/code&gt; handler takes an &lt;code&gt;engine&lt;/code&gt; and a &lt;code&gt;eventHandler&lt;/code&gt; parameter. The &lt;code&gt;engine&lt;/code&gt; is the underlying IMG.LY Engine object we’ve been using. The &lt;code&gt;eventHandler&lt;/code&gt; lets us send information back to the Video Editor UI about the progress or any errors.&lt;/p&gt;
&lt;p&gt;In the documentation, there is a helper function to export the video. After it runs it returns the &lt;code&gt;data&lt;/code&gt; of the exported video and a &lt;code&gt;mimeType&lt;/code&gt;, which will be &lt;code&gt;.mp4&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; data: Date, mimeType: MIMEType&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; mode &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; try&lt;/span&gt;&lt;span&gt; mainEngine.scene.&lt;/span&gt;&lt;span&gt;getMode&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;guard&lt;/span&gt;&lt;span&gt; mode &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; .video &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; CallbackError.unknownSceneMode; &lt;/span&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;(data, mimeType) &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; try&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; exportVideo&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code in the documentation handles static files from the &lt;code&gt;DesignEditor&lt;/code&gt; as well as videos from the &lt;code&gt;VideoEditor&lt;/code&gt;. The above code makes it so that only video can be exported, since our app only works with video.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;exportVideo&lt;/code&gt; function, the code first checks to make sure it has a properly formed &lt;code&gt;scene&lt;/code&gt; object to work with. Then it starts the export using &lt;code&gt;mainEngine.block.exportVideo(_, mimeType:)&lt;/code&gt;. This begins an async throwing stream during the export. The VideoExport objects in the stream are either &lt;code&gt;.progress&lt;/code&gt; or &lt;code&gt;.finished&lt;/code&gt;. When a &lt;code&gt;.progress&lt;/code&gt; object arrives, the &lt;code&gt;exportVideo&lt;/code&gt; function sends an update to the &lt;code&gt;eventHandler&lt;/code&gt; to display in the UI. When the &lt;code&gt;.finished&lt;/code&gt; object arrives, it has an associated object that is the data for the video.&lt;/p&gt;
&lt;p&gt;Now instead of opening the share sheet, we can write the video to the documents directory for the app.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      guard&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; documentDirectory &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; try?&lt;/span&gt;&lt;span&gt; FileManager.default.&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt;: .documentDirectory, &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;: .userDomainMask, &lt;/span&gt;&lt;span&gt;appropriateFor&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;create&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;        else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          logger.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Documents directory not found&quot;&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; filePath &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; documentDirectory.&lt;/span&gt;&lt;span&gt;appending&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;component&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;UUID&lt;/span&gt;&lt;span&gt;().uuidString)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  try&lt;/span&gt;&lt;span&gt; data.&lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;: filePath)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; error &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; NSError) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  logger.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;could not write finished file: &lt;/span&gt;&lt;span&gt;\(error.&lt;/span&gt;&lt;span&gt;localizedDescription&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;eventHandler.&lt;/span&gt;&lt;span&gt;send&lt;/span&gt;&lt;span&gt;(.&lt;/span&gt;&lt;span&gt;exportCompleted&lt;/span&gt;&lt;span&gt; {})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code above takes the &lt;code&gt;data&lt;/code&gt; from the &lt;code&gt;exportVideo&lt;/code&gt; function and writes it to the app’s documents directory, giving it a UUID string value as a filename. Then it updates the UI to let it know the export is done but sends an empty action block.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-a-playback-controller&quot;&gt;Setting Up a Playback Controller&lt;/h2&gt;
&lt;p&gt;For this example app, the playback controller will play any clips in the app’s &lt;code&gt;Documents&lt;/code&gt; directory. Each clip plays on a loop. The user can swipe up to get the next clip and tap to pause or restart the clip. If there are no clips, the user will be encouraged to make a new one.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;screen shot of player&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 963px) 963px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;963&quot; height=&quot;982&quot; src=&quot;https://img.ly/_astro/viewer_Z21kKPe.webp&quot; srcset=&quot;/_astro/viewer_WarKA.webp 640w, /_astro/viewer_2gQt5P.webp 750w, /_astro/viewer_2mRkuK.webp 828w, /_astro/viewer_Z21kKPe.webp 963w&quot;&gt;&lt;/p&gt;
&lt;p&gt;When the video player view appears, the first task is to see if there are any videos to load. Any video files in the Documents directory are assumed to be ready to play.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;func&lt;/span&gt;&lt;span&gt; loadVideos&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //get a handle to the documents directory for this app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  guard&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; documentDirectory &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; try?&lt;/span&gt;&lt;span&gt; FileManager.default.&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt;: .documentDirectory, &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;: .userDomainMask, &lt;/span&gt;&lt;span&gt;appropriateFor&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;create&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;else&lt;/span&gt;&lt;span&gt; { logger.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Documents directory not found&quot;&lt;/span&gt;&lt;span&gt;); &lt;/span&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 class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; files &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; try?&lt;/span&gt;&lt;span&gt; FileManager.default.&lt;/span&gt;&lt;span&gt;contentsOfDirectory&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;at&lt;/span&gt;&lt;span&gt;: documentDirectory, &lt;/span&gt;&lt;span&gt;includingPropertiesForKeys&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    videos &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; files&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;The code above reads all of the filenames from the documents directory into a &lt;code&gt;files&lt;/code&gt; array and then assigns that array to a &lt;code&gt;videos&lt;/code&gt; variable.&lt;/p&gt;
&lt;p&gt;Each time the app is launched or the &lt;code&gt;VideoEditor&lt;/code&gt; view is dismissed, the video player view will get the &lt;code&gt;onAppear&lt;/code&gt; message. This is a good place to check for videos using the &lt;code&gt;loadVideos&lt;/code&gt; function. After the videos are loaded, the app can start to play the first one.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;func&lt;/span&gt;&lt;span&gt; setupVideoPlayer&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  guard&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; currentVideo &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; videos.&lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;span&gt; else&lt;/span&gt;&lt;span&gt; {logger.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;No videos to play&quot;&lt;/span&gt;&lt;span&gt;); &lt;/span&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 class=&quot;line&quot;&gt;&lt;span&gt;  self&lt;/span&gt;&lt;span&gt;.shouldHideEmptyDirectoryText &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; true&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; endMonitor &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; NotificationCenter.default.&lt;/span&gt;&lt;span&gt;publisher&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt;: NSNotification.Name.AVPlayerItemDidPlayToEndTime)&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;  self&lt;/span&gt;&lt;span&gt;.player &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVPlayerItem&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;: currentVideo)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  self&lt;/span&gt;&lt;span&gt;.currentVideo &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; currentVideo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the code above, we first check to see if there are any video files. If there are, then hide the message about videos and create a new &lt;code&gt;AVPlayerItem&lt;/code&gt; using the &lt;code&gt;URL&lt;/code&gt; of the first video. The &lt;code&gt;endMonitor&lt;/code&gt;, &lt;code&gt;shouldHideEmptyDirectoryText&lt;/code&gt;, and &lt;code&gt;player&lt;/code&gt; are both being watched by the &lt;code&gt;View&lt;/code&gt;. The AVPlayer gets rendered in a regular SwiftUI View.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;VideoPlayer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;player&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;frame&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;640&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;360&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;onAppear&lt;/span&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;play&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;onReceive&lt;/span&gt;&lt;span&gt;(endMonitor) { &lt;/span&gt;&lt;span&gt;_&lt;/span&gt;&lt;span&gt; in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    player.&lt;/span&gt;&lt;span&gt;seek&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;: .zero)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    player.&lt;/span&gt;&lt;span&gt;play&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the video reaches the end, it will &lt;code&gt;seek(to: .zero)&lt;/code&gt; which rewinds the video and loops it.&lt;/p&gt;
&lt;p&gt;In the TikTok app, you can advance to the next video by swiping up. Additionally, you can start and stop any video by tapping on the screen. We can add both of those features using gesture recognizers.&lt;/p&gt;
&lt;p&gt;The code for play and pause is attached to a Tap Gesture modifier we can append to the player.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onTapGesture&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; player.rate &lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; 0.0&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    player.rate &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    player.rate &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 1.0&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 can just adjust the playback rate. A value of &lt;code&gt;1.0&lt;/code&gt; is a normal speed and &lt;code&gt;0.0&lt;/code&gt; is paused. The player also has a &lt;code&gt;.pause&lt;/code&gt; and &lt;code&gt;.play&lt;/code&gt; methods, but sometimes these seem buggy.&lt;/p&gt;
&lt;p&gt;Swipe is a little more difficult since SwiftUI only has swiping on List items. So we can use a &lt;code&gt;drag&lt;/code&gt; gesture and then evaluate the translations when it’s complete.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;gesture&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    DragGesture&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;minimumDistance&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;3.0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;coordinateSpace&lt;/span&gt;&lt;span&gt;: .local)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        .&lt;/span&gt;&lt;span&gt;onEnded&lt;/span&gt;&lt;span&gt; { value &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            // Check for a vertical, upward swipe with minimal horizontal deviation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          if&lt;/span&gt;&lt;span&gt; abs&lt;/span&gt;&lt;span&gt;(value.translation.width) &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; 100&lt;/span&gt;&lt;span&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;             value.translation.height &lt;/span&gt;&lt;span&gt;&amp;#x3C;&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;                // It was an up swipe!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;		viewModel.&lt;/span&gt;&lt;span&gt;advanceVideo&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then advancing the video is a simple matter of refreshing the &lt;code&gt;player&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;func&lt;/span&gt;&lt;span&gt; advanceVideo&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  guard&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; currentVideo &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; self&lt;/span&gt;&lt;span&gt;.currentVideo &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; { logger.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;No current video.&quot;&lt;/span&gt;&lt;span&gt;); &lt;/span&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 class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; currentIndex &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; videos.&lt;/span&gt;&lt;span&gt;firstIndex&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;span&gt;: currentVideo)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  var&lt;/span&gt;&lt;span&gt; nextVideo &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; videos.&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;after&lt;/span&gt;&lt;span&gt;: currentIndex&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;  if&lt;/span&gt;&lt;span&gt; nextVideo &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; videos.&lt;/span&gt;&lt;span&gt;count&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    nextVideo &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&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;  self&lt;/span&gt;&lt;span&gt;.currentvideo &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; videos[nextVideo]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  self&lt;/span&gt;&lt;span&gt;.player &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVPlayer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.currentVideo&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code will get the next file URL from the array of videos. When it reaches the end, it will loop back and get the file at index 0. Then it creates a new player. Because the view is observing that &lt;code&gt;player&lt;/code&gt; variable, it will update with the new video.&lt;/p&gt;
&lt;p&gt;With this view, the user can create new videos by tapping the creation button and swipe to view all of their created videos.&lt;/p&gt;
&lt;h2 id=&quot;where-to-go-from-here&quot;&gt;Where to Go From Here&lt;/h2&gt;
&lt;p&gt;This tutorial has focused on how the CreativeEditor SDK can help you quickly make a video creation app like TikTok. Good next steps would be to further customize the editing tools and build out the network for sharing and tagging videos. Something that is important to consider is the data structure for each video. The iOS system is optimized to read only as much of a video file off of disk as is needed at any moment. Your app should use those optimizations to run faster. So, don’t load the whole video into memory as a &lt;code&gt;Data&lt;/code&gt; object. Your data structures should keep the large video files somehow separate from the much smaller metadata (likes, comments, etc.). Consider storing the &lt;code&gt;URL&lt;/code&gt; or filename of the video in the same object as the likes and comments. This will also allow you to cache video files after they have been downloaded so that you don’t need to redownload them when other data such as comments or number of likes changes.&lt;/p&gt;
&lt;p&gt;Thanks for reading! We hope that you’ve gotten a better understanding for how a tool like the &lt;a href=&quot;https://img.ly/products/creative-sdk&quot;&gt;CreativeEditor SDK&lt;/a&gt; can bring your ideas to market faster. Feel free to reach out to us with any questions, comments, or suggestions.&lt;/p&gt;
&lt;p&gt;Looking for more video capabilities? Check out our solutions for &lt;a href=&quot;https://img.ly/use-cases/story-reels-short-video-creation&quot;&gt;Short Video Creation&lt;/a&gt;, and &lt;a href=&quot;https://img.ly/products/camera-sdk&quot;&gt;Camera SDK&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;To stay in the loop, subscribe to our&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;&lt;strong&gt;Newsletter&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;or follow us on&lt;/strong&gt; &lt;a href=&quot;https://www.linkedin.com/company/img.ly&quot;&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;and&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;.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Walter</dc:creator><media:content url="https://blog.img.ly/2023/03/Build_TikTok_app_iOS_VE-SDK-1.jpg" medium="image"/><category>How-To</category><category>iOS</category><category>Video Editing</category><category>Social Media</category><category>Tech</category><category>Learning</category><category>Tutorial</category></item><item><title>How to Add Stickers and Overlays to a Video in Flutter</title><link>https://img.ly/blog/how-to-add-stickers-and-overlays-to-a-video-in-flutter/</link><guid isPermaLink="true">https://img.ly/blog/how-to-add-stickers-and-overlays-to-a-video-in-flutter/</guid><description>Learn how to apply stickers and overlays to a video in Flutter and make your app&apos;s video content more personalizable and user-oriented. </description><pubDate>Fri, 24 Mar 2023 09:32:33 GMT</pubDate><content:encoded>&lt;p&gt;When working with interactive content, it may happen that you will need to add remote or local assets on top of your video file. The assets varying from images and stickers to videos and fonts are usually grouped and deployed within your application and are available at runtime. This tutorial will teach you how to apply stickers and overlays to a video in Flutter and make your video content more personalizable and user-oriented. We start by considering the prerequisites for successful video integration, then talk about how Flutter manages overlays in order to personalize our video file.&lt;/p&gt;
&lt;h2 id=&quot;how-does-flutter-handle-video-assets&quot;&gt;How does Flutter handle video assets?&lt;/h2&gt;
&lt;p&gt;Unlike images, Flutter displays a video file by employing a special &lt;strong&gt;&lt;code&gt;video_player&lt;/code&gt;&lt;/strong&gt; plugin that provides playback, stores a file, and manages speed and sound control. For iOS-based applications, the video is integrated via &lt;a href=&quot;https://developer.apple.com/documentation/avfoundation/avplayer&quot;&gt;AVPlayer&lt;/a&gt;, whereas the Android system employs &lt;a href=&quot;https://developer.android.com/media/media3/exoplayer&quot;&gt;ExoPlayer&lt;/a&gt;. Crucially, to create a simple video player, one should follow the steps below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First, to ensure that pubspec.yaml file contains the &lt;code&gt;video_player&lt;/code&gt; dependency;&lt;/li&gt;
&lt;li&gt;Then permit access to videos for the application;&lt;/li&gt;
&lt;li&gt;Make a &lt;code&gt;VideoPlayerController&lt;/code&gt; and set it up;&lt;/li&gt;
&lt;li&gt;Finally, don’t forget to display and play the video player;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, let’s closely look at the setup process.&lt;/p&gt;
&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites:&lt;/h3&gt;
&lt;p&gt;We start by adding the following dependencies of &lt;code&gt;video_player&lt;/code&gt; plugin to pubspec.yaml file.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dependencies&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  flutter&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    sdk&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;flutter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  video_player&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;^2.4.7&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Afterwards, change configurations of both &lt;code&gt;android&lt;/code&gt; and &lt;code&gt;ios&lt;/code&gt; systems to guarantee that an application has the proper authorization for video streaming: e.g. for Android-oriented programs, proceed to the &lt;code&gt;&amp;#x3C;project root&gt;/android/app/src/main/AndroidManifest.xml&lt;/code&gt; directory – where it’s necessary to add the next line to the &lt;code&gt;AndroidManifest.xml&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;xml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;manifest&lt;/span&gt;&lt;span&gt; xmlns:android&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&amp;#x3C;http://schemas.android.com/apk/res/android&gt;&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;application&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 class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span&gt;application&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 class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;uses-permission&lt;/span&gt;&lt;span&gt; android:name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;android.permission.INTERNET&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;manifest&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For iOS-based apps the &lt;code&gt;Info.plist&lt;/code&gt; file is stored at &lt;code&gt;&amp;#x3C;project root&gt;/ios/Runner/Info.plist&lt;/code&gt;. Once again, don’t forget to add the following specification to the original code:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;xml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;&gt;NSAppTransportSecurity&amp;#x3C;/&lt;/span&gt;&lt;span&gt;key&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;dict&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;key&lt;/span&gt;&lt;span&gt;&gt;NSAllowsArbitraryLoads&amp;#x3C;/&lt;/span&gt;&lt;span&gt;key&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;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;dict&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Successful video integration in Flutter is managed through the &lt;code&gt;VideoPlayer&lt;/code&gt; widget and a &lt;code&gt;VideoPlayerController&lt;/code&gt;, which sets the connection to the asset and &lt;code&gt;initialize&lt;/code&gt; the controller for playback.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/// Stateful widget to fetch and then display video content.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; VideoApp&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; StatefulWidget&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; VideoApp&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;span&gt;Key&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; key}) &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; super&lt;/span&gt;&lt;span&gt;(key&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; key);&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;  @override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  _VideoAppState&lt;/span&gt;&lt;span&gt; createState&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; _VideoAppState&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;class&lt;/span&gt;&lt;span&gt; _VideoAppState&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; State&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;VideoApp&lt;/span&gt;&lt;span&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  late&lt;/span&gt;&lt;span&gt; VideoPlayerController&lt;/span&gt;&lt;span&gt; _controller;&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;  @override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  void&lt;/span&gt;&lt;span&gt; initState&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;initState&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    _controller &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; VideoPlayerController&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;asset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;assets/bee.mp4&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;      /// Specify the path to your video asset here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      ..&lt;/span&gt;&lt;span&gt;initialize&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        /// Ensure the first frame is shown after the video is initialized,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        /// even before the play button has been pressed.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        setState&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;  @override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  void&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;    super&lt;/span&gt;&lt;span&gt;.&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;    _controller.&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that video tends to take up as much space on the screen as possible by default, which can significantly deteriorate the video’s quality. Therefore, the Flutter team suggests employing the &lt;code&gt;AspectRatio&lt;/code&gt;widget to adjust the video proportions.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; AspectRatio&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    aspectRatio&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; _controller.value.aspectRatio)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;adding-overlay-stickers-and-text-to-a-video-in-flutter&quot;&gt;Adding Overlay Stickers and Text to a Video in Flutter&lt;/h2&gt;
&lt;p&gt;To see the complete code of our demo application, you can clone the &lt;a href=&quot;https://github.com/nataliakzm/Adding_Stickers_and_Overlays_to_video_in_Flutter&quot;&gt;GitHub repository&lt;/a&gt; or put the following command to your Terminal:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; clone&lt;/span&gt;&lt;span&gt; git@github.com:nataliakzm/Adding_Stickers_and_Overlays_to_video_in_Flutter.git&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/add-overlay-flutter.mp4&quot; controls muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Similarly to how we handled &lt;a href=&quot;https://img.ly/blog/how-to-resize-images-in-flutter/&quot;&gt;resizing images in Flutter&lt;/a&gt;,  the same method is used when overlaying an object on a video.  The above example consists basically of three containers, one with a sticker, another with the textual watermark and the last with a local video asset.  However, the critical difference here is that a &lt;code&gt;Container&lt;/code&gt; filled with a sticker and a &lt;code&gt;Container&lt;/code&gt; filled with a textual watermark cannot be simply listed one by one in your code but rather must be both placed within a &lt;code&gt;Stack&lt;/code&gt; widget.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Stack&lt;/code&gt; container is designed as a “mother”-widget which retains multiple layers of widgets on the screen. &lt;code&gt;Stack&lt;/code&gt; structures these branches of children into a hierarchical system from bottom to top. Thus, the uppermost widget goes to the background, and the bottommost item is displayed in the foreground.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Stack&lt;/span&gt;&lt;span&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&gt;&lt;span&gt; &amp;#x3C;&lt;/span&gt;&lt;span&gt;Widget&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;    BottomWidget&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    MiddleWidget&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    TopWidget&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ]),&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since the size of the &lt;code&gt;Stack&lt;/code&gt; widget is aimed to be the largest size among layers, we will place our video asset on this layer. Then, to implement the overlays, it’s essential to position and/or align each child’s container.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Stack&lt;/span&gt;&lt;span&gt;(children&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;  VideoPlayer&lt;/span&gt;&lt;span&gt;(_controller),&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;  /// Don&apos;t forget to align the position of the Сontainer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Positioned&lt;/span&gt;&lt;span&gt;( bottom&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;span&gt;, left&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 10&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Container&lt;/span&gt;&lt;span&gt;( width&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 80&lt;/span&gt;&lt;span&gt;, height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 40&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;        /// In case you want to check the position/size of the Container uncomment the next line&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        //color: Color(0xff0360da),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Align&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            alignment&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Alignment&lt;/span&gt;&lt;span&gt;.center,&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;            /// Integrate Sticker overlay into a video&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Image&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;asset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;assets/sticker.png&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;  Positioned&lt;/span&gt;&lt;span&gt;( top&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;span&gt;, right&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 10&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Container&lt;/span&gt;&lt;span&gt;( width&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 80&lt;/span&gt;&lt;span&gt;, height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 40&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Align&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            alignment&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Alignment&lt;/span&gt;&lt;span&gt;.center,&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;            /// Integrate Text overlay into a video&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Text&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;IMG.LY&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                style&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; TextStyle&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Colors&lt;/span&gt;&lt;span&gt;.white,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    fontSize&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 20&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    fontWeight&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; FontWeight&lt;/span&gt;&lt;span&gt;.bold))))),&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 there is no video, a blank page will be return   : Container()),&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;summing-up&quot;&gt;Summing up&lt;/h2&gt;
&lt;p&gt;Thus, you can see from the code below that integrating multiple layers over interactive assets can be a tricky task which requires bearing in mind the position and order of each layer, as well as managing how the children are aligned to a video file and the screen size of the user. Conversely, the discussed method allows personalizing an application without significant modifications to the original file.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &apos;package:flutter/cupertino.dart&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;package:flutter/material.dart&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;/// Import video_player package to integrate a video asset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &apos;package:video_player/video_player.dart&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;void&lt;/span&gt;&lt;span&gt; main&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; runApp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; VideoApp&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;/// Stateful widget to fetch and then display video content.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; VideoApp&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; StatefulWidget&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; VideoApp&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;span&gt;Key&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; key}) &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; super&lt;/span&gt;&lt;span&gt;(key&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; key);&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;  @override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  _VideoAppState&lt;/span&gt;&lt;span&gt; createState&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; _VideoAppState&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;class&lt;/span&gt;&lt;span&gt; _VideoAppState&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; State&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;VideoApp&lt;/span&gt;&lt;span&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  late&lt;/span&gt;&lt;span&gt; VideoPlayerController&lt;/span&gt;&lt;span&gt; _controller;&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;  @override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  void&lt;/span&gt;&lt;span&gt; initState&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;initState&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    _controller &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; VideoPlayerController&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;asset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;assets/bee.mp4&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;      /// Specify the path to your video asset here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      ..&lt;/span&gt;&lt;span&gt;initialize&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        /// Ensure the first frame is shown after the video is initialized,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        /// even before the play button has been pressed.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        setState&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;  @override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Widget&lt;/span&gt;&lt;span&gt; build&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;BuildContext&lt;/span&gt;&lt;span&gt; context) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; MaterialApp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        debugShowCheckedModeBanner&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;        home&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Scaffold&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            appBar&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; AppBar&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                title&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Text&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;How to add Stickers and Overlays&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                backgroundColor&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Color&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0xff0360da&lt;/span&gt;&lt;span&gt;)),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            body&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Center&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; _controller.value.isInitialized&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;                    /// First, we specify the AspectRatio of a video frame&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    ?&lt;/span&gt;&lt;span&gt; AspectRatio&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        aspectRatio&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; _controller.value.aspectRatio,&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;                        /// Then, we initialize a Stack widget&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Stack&lt;/span&gt;&lt;span&gt;(children&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;                          VideoPlayer&lt;/span&gt;&lt;span&gt;(_controller),&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;                          /// Don&apos;t forget to align the position of the Сontainer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                          Positioned&lt;/span&gt;&lt;span&gt;( bottom&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;span&gt;, left&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 10&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                              child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Container&lt;/span&gt;&lt;span&gt;( width&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 80&lt;/span&gt;&lt;span&gt;, height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 40&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;                                  /// Uncomment the following line to check the position/size of the Container&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                  //color: Color(0xff0360da),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                  child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Align&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                      alignment&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Alignment&lt;/span&gt;&lt;span&gt;.center,&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;                                      /// Integrate Image overlay into a video&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                      child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Image&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;asset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;assets/sticker.png&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;                          Positioned&lt;/span&gt;&lt;span&gt;( top&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;span&gt;, right&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 10&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                              child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Container&lt;/span&gt;&lt;span&gt;( width&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 80&lt;/span&gt;&lt;span&gt;, height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 40&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                  child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Align&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                      alignment&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Alignment&lt;/span&gt;&lt;span&gt;.center,&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;																			/// Integrate some Text overlay into a video&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                      child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Text&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;IMG.LY&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                          style&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; TextStyle&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                              color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Colors&lt;/span&gt;&lt;span&gt;.white,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                              fontSize&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 20&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                              fontWeight&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; FontWeight&lt;/span&gt;&lt;span&gt;.bold))))),&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 there is no video, a blank page will be return&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    :&lt;/span&gt;&lt;span&gt; Container&lt;/span&gt;&lt;span&gt;()),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            floatingActionButton&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; FloatingActionButton&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;extended&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                backgroundColor&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; const&lt;/span&gt;&lt;span&gt; Color&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0xff0360da&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                foregroundColor&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Colors&lt;/span&gt;&lt;span&gt;.white,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                onPressed&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;                  setState&lt;/span&gt;&lt;span&gt;(() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    _controller.value.isPlaying&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        ?&lt;/span&gt;&lt;span&gt; _controller.&lt;/span&gt;&lt;span&gt;pause&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; _controller.&lt;/span&gt;&lt;span&gt;play&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;                icon&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Icon&lt;/span&gt;&lt;span&gt;(_controller.value.isPlaying&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    ?&lt;/span&gt;&lt;span&gt; Icons&lt;/span&gt;&lt;span&gt;.pause&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    :&lt;/span&gt;&lt;span&gt; Icons&lt;/span&gt;&lt;span&gt;.play_arrow),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                label&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Text&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Play for IMG.LY&apos;&lt;/span&gt;&lt;span&gt;))));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  @override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  void&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;    super&lt;/span&gt;&lt;span&gt;.&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;    _controller.&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;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;adding-overlays-to-a-video-with-flutter-package-for-videoeditor-sdk&quot;&gt;Adding Overlays to a Video with &lt;a href=&quot;https://pub.dev/packages/video_editor_sdk/install&quot;&gt;Flutter package for VideoEditor SDK&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;However, if you would like to offer more advanced sticker and overlay functionality to your user and you might need a more complex UI structure. Since this can be an extremely time consuming undertaking you might want to opt for a ready-to-use solution such as &lt;a href=&quot;https://img.ly/products/video-sdk?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;VideoEditor SDK&lt;/a&gt; which among a host of other essential video editing features allows users to easily add stickers and different overlays to their videos.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/vesdk-demo-overlay.mp4&quot; controls muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;To replicate the example above, go to the &lt;a href=&quot;https://img.ly/docs/vesdk/flutter/getting-started/&quot;&gt;official documentation&lt;/a&gt; to discover how to get started with VideoEditor SDK or follow our guide on how to integrate &lt;a href=&quot;https://img.ly/blog/a-modern-video-editor-sdk-for-your-flutter-app/&quot;&gt;video editor for Flutter&lt;/a&gt; into your app. Try to upload your video file and modify it with various stickers, text and other overlays.  Alternatively, you can download our free mobile demo app from the &lt;a href=&quot;https://apps.apple.com/us/app/img-ly-photo-video-editor/id589839231&quot;&gt;App Store&lt;/a&gt; or &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.photoeditorsdk.android.app&quot;&gt;Google Play&lt;/a&gt;, and test a comprehensive set video editing tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! To stay in the loop, subscribe to our &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;Newsletter&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Natalia</dc:creator><media:content url="https://blog.img.ly/2022/11/sticker-in-flutter-video.png" medium="image"/><category>How-To</category><category>Flutter</category><category>Video Editing</category><category>Tech</category><category>Tutorial</category></item><item><title>Variable Data Printing: What Is It &amp; Why Do You Need It</title><link>https://img.ly/blog/variable-data-printing-definition-and-benefits/</link><guid isPermaLink="true">https://img.ly/blog/variable-data-printing-definition-and-benefits/</guid><description>Discover the power of VDP for automating processes, scaling personalization &amp; enhancing intimacy in marketing, advertising &amp; customer management.</description><pubDate>Fri, 03 Mar 2023 09:45:33 GMT</pubDate><content:encoded>&lt;p&gt;Variable data printing (VDP) has been around since the 1980s, helping businesses automate manual processes, scale personalization efforts, and utilize the power of direct mail marketing. While the term “variable data printing” may not be familiar to everyone, we encounter its use regularly in our day-to-day lives through bills, event tickets, or membership cards.&lt;/p&gt;
&lt;p&gt;VDP’s ability to leverage data and create personalized tangible items, such as postcards or brochures, provides a more intimate experience compared to digital communication. Although process automation is a significant advantage of VDP, there are many other ways to use it.&lt;/p&gt;
&lt;p&gt;In this article, we’ll explore the most common applications of VDP and the best practices for working with it.&lt;/p&gt;
&lt;h2 id=&quot;how-it-works&quot;&gt;How It Works&lt;/h2&gt;
&lt;p&gt;To fully understand the advantages of variable data printing (VDP), let’s start with a clear definition. VDP, also referred to as variable data imaging, is a digital printing technique that enables the creation of numerous customized materials in a single print run. This process is powered by software that can generate data-driven designs or templates by pulling variables from databases or files.&lt;/p&gt;
&lt;p&gt;The variables used in VDP can automatically generate unique and personalized materials based on data sources. Anything that can be customized, such as names, addresses, messages, or images, can be utilized as a variable. These elements can be changed on each printed piece without slowing down the printing process.&lt;/p&gt;
&lt;p&gt;To illustrate how VDP works in practice, let’s consider a scenario where a company offers a loyalty program for customers to collect points when they make purchases. To encourage more spending, customers can use their accumulated points to receive discounts or freebies. Although points and bonuses can be accessed through an app or website, the company can create immediate gamification by sending a personalized card with every new purchase.&lt;/p&gt;
&lt;p&gt;To personalize that card, we are looking at three separate variables: name, number of points, and type of discount that can be applied. To manually generate all variations of the same template would be counterproductive, but with variables connected to a database it’s an automated process. A name is connected to a number of points, which in turn are connected to discounts. The software does all the heavy lifting, while marketers and designers can focus on other tasks.&lt;/p&gt;
&lt;h2 id=&quot;common-applications-of-vdp&quot;&gt;Common Applications of VDP&lt;/h2&gt;
&lt;p&gt;Although VDP was originally used to automate manual mail processes, its usefulness extends beyond that. Its ability to personalize marketing and advertising materials quickly and efficiently has made it widely appreciated and used in various industries.&lt;/p&gt;
&lt;p&gt;While there are many ways to use VDP, we’ll focus on its most common applications in marketing, advertising, and customer relationship management. These areas benefit greatly from the ability to tailor messages to specific audiences and create unique, personalized experiences.&lt;/p&gt;
&lt;h3 id=&quot;marketing&quot;&gt;Marketing&lt;/h3&gt;
&lt;p&gt;From quickly generating business cards to creating custom QR codes, variable data printing is a big part of marketing efforts for most businesses. Apart from handling internal processes, VPD also has a hand in scaling personalized marketing campaigns. The growing preference for personalized messaging and products goes alongside increased usage of VDP.&lt;/p&gt;
&lt;p&gt;Production of personalized labels, packaging, or brochures is one of the best ways to leverage VDP in marketing. It can be used as a step in a broader personalization campaign, like generating custom QR codes to bring users to personalized landing pages. Or be a venture on its own, like &lt;a href=&quot;https://us.coca-cola.com/store/personalized-bottle&quot;&gt;“Share a Coke”&lt;/a&gt; by Coca-Cola and its widely successful personalization of labels.&lt;/p&gt;
&lt;h3 id=&quot;advertising&quot;&gt;Advertising&lt;/h3&gt;
&lt;p&gt;Variable data printing brought new life to direct mail, moving away from what once was perceived as junk and irrelevant advertising, and bringing about a new age of valuable and relatable mail. A &lt;a href=&quot;https://www.fundera.com/resources/direct-mail-statistics&quot;&gt;recent study&lt;/a&gt; reported that 70% of consumers see direct mail as more personal than online interactions, with 54% claiming they want direct mail from brands that interest them. Moreover, direct mail has the highest response rate among all advertising channels being five to nine times higher than other forms, with a simple act of adding a name to a direct mail piece that can increase response rates by 135%.&lt;/p&gt;
&lt;p&gt;Taking advantage of VPD in advertising usually includes customized leaflets or coupons with images and content based on age or demographics. One of the personalization examples is including custom maps with steps on how to reach an advertised location, making it much more relatable and easy to act on.&lt;/p&gt;
&lt;h3 id=&quot;customer-relationship-management&quot;&gt;Customer Relationship Management&lt;/h3&gt;
&lt;p&gt;While VDP helps marketing and advertising drive acquisition, it’s also a great way to help with customer retention. Small and yet important gestures of appreciation like custom-made thank you and birthday cards can go a long way. As mentioned before, VDP can also be used to generate membership cards or personalized bonuses and discounts based on shopping preferences. Tapping into the power of variables, every printed material sent to a customer can be created in a way that will feel personal and appreciative, in turn helping sustain loyal customers.&lt;/p&gt;
&lt;h2 id=&quot;why-use-vdp&quot;&gt;Why Use VDP&lt;/h2&gt;
&lt;p&gt;Variable data printing is not reserved for a single industry or business type. It can be leveraged across multiple customer touchpoints, from the awareness stage via direct mail advertising, to a personalized label and thank you card shipped with a purchased product. VDP can also be used internally to show gratitude and appreciation to employees with custom-made cards and packaging. In fact, variable data printing can be used on any piece of printed content or design that can be personalized.&lt;/p&gt;
&lt;h3 id=&quot;benefits&quot;&gt;Benefits&lt;/h3&gt;
&lt;p&gt;The advantages of variable data printing go hand-in-hand with the advantages of personalization. &lt;a href=&quot;https://www.twilio.com/blog/announcing-the-state-of-personalization-2021&quot;&gt;Studies show&lt;/a&gt; that customers respond much better to marketing materials they can connect to, with 60% of consumers saying they will likely become repeat buyers after a personalized shopping experience with a retailer.&lt;/p&gt;
&lt;p&gt;VDP can help create a valuable experience from start to finish and facilitate a meaningful connection between a brand and a customer. Some of the most prominent benefits are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Providing personalized shopping experience in the form of custom labels, packaging &amp;#x26; cards&lt;/li&gt;
&lt;li&gt;Building strong customer relationships with personalized thank you &amp;#x26; birthday cards, custom messaging, and discounts&lt;/li&gt;
&lt;li&gt;Delivering meaningful direct mail advertising with higher conversion rates&lt;/li&gt;
&lt;li&gt;Generating personalized marketing campaigns &amp;#x26; materials that customers can connect with&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bringing variable data printing to your workflows, whether it concerts marketing, advertising, HR, or sales, is a great way to tap into &lt;a href=&quot;https://img.ly/blog/why-companies-need-to-go-personal/&quot;&gt;powerful benefits of personalization&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;challenges&quot;&gt;Challenges&lt;/h3&gt;
&lt;p&gt;The main challenge of VDP is having software that can efficiently work with variables and generate print-ready designs, while also providing advanced design editing features.&lt;/p&gt;
&lt;p&gt;Since the popularization of personalization, variables can be leveraged in many different types of marketing materials. However, the design of those materials is equally important, and so it’s an automated print validation process.&lt;/p&gt;
&lt;p&gt;This challenge applies to companies that use variable data to generate designs, as well as Print Service Providers (PSP) working with VDP. For the PSP, another challenge is file format submission, especially if they don’t have a web-to-print editor their customers can use to submit their orders. Flawed print validation, incompatible format, low resolution, or insufficient quality could be costly mistakes to make. Hence, the best option for print service providers is to develop their own web-to-print editor with variable data printing that will take care of those problems and limit manual work to a minimum.&lt;/p&gt;
&lt;h2 id=&quot;example-of-leveraging-variable-data-printing-with-cesdk&quot;&gt;Example of Leveraging Variable Data Printing with CE.SDK&lt;/h2&gt;
&lt;p&gt;IMG.LY has developed CreativeEditor SDK to help integrate &lt;a href=&quot;https://img.ly/use-cases/variable-data-printing&quot;&gt;VDP&lt;/a&gt; into all designs in an easy and intuitive way. &lt;a href=&quot;https://img.ly/products/creative-sdk&quot;&gt;CE.SDK&lt;/a&gt; is fully customizable and can be integrated into a customer-facing web-to-print editor, providing powerful editing features, or an in-house design editor for all creative needs. Here’s an example of how you can leverage variables with our editor.&lt;/p&gt;
&lt;h3 id=&quot;creating-custom-thank-you-card&quot;&gt;Creating Custom Thank-You Card&lt;/h3&gt;
&lt;p&gt;As mentioned before, thank you cards are a great way to show appreciation and build stronger connections with both customers and employees. Creation of a custom thank-you card is pretty straightforward, here are the necessary steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Creating a Smart Template&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To create a personalized card, we begin by designing a template with variable fields. The front design remains consistent, while the back features unique personalized messages.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Design a template by adding images, text, or fun shapes and stickers.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1120px) 1120px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1120&quot; height=&quot;598&quot; src=&quot;https://img.ly/_astro/01_create_template-1_f1nPB.webp&quot; srcset=&quot;/_astro/01_create_template-1_DshRl.webp 640w, /_astro/01_create_template-1_Z23vRzl.webp 750w, /_astro/01_create_template-1_Z22g4xm.webp 828w, /_astro/01_create_template-1_Z1k7Jmg.webp 1080w, /_astro/01_create_template-1_f1nPB.webp 1120w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Assigning Placeholders&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The next step is to choose the elements that will be personalized. For example, we may select a personalized greeting, such as “Hello {{name}}”, and address information. To insert a variable, we use double curly brackets {{}} and a placeholder name, such as {{firstname}} or {{address}}.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Take advantage of variables that serve as placeholders for information, such as customer names.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1120px) 1120px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1120&quot; height=&quot;527&quot; src=&quot;https://img.ly/_astro/02_assigning_placeholders_1MctnI.webp&quot; srcset=&quot;/_astro/02_assigning_placeholders_Z1dqOUT.webp 640w, /_astro/02_assigning_placeholders_i4vNS.webp 750w, /_astro/02_assigning_placeholders_Z1g3EsX.webp 828w, /_astro/02_assigning_placeholders_ZQmM65.webp 1080w, /_astro/02_assigning_placeholders_1MctnI.webp 1120w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Connecting Template to a Data Source&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The last step is connecting your template to a data source with list of variables. This can be done by using the &lt;a href=&quot;https://img.ly/docs/cesdk/js/create-templates/add-dynamic-content/text-variables-7ecb50/&quot;&gt;Creative Engine variable API&lt;/a&gt; to programmatically set values for the variables and render the final personalized card.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Design Validation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In the example above, there shouldn’t be any issue with text exceeding beyond bleed line or other common print errors, however, design validation is an important feature of CE.SDK. It will automatically detect any issues that might cause a print error, cause trademark infringement or generate harmful content.&lt;/p&gt;
&lt;p&gt;And that’s it! We have created a template for custom thank-you card in three easy steps. Here is what a card would look like.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;A personalized hello: Create a custom card yourself with CE.SDK.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 621px) 621px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;621&quot; height=&quot;896&quot; src=&quot;https://img.ly/_astro/03_design_validation_Z2gOUY2.webp&quot; srcset=&quot;/_astro/03_design_validation_Z2gOUY2.webp 621w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Try creating a custom card with CE.SDK yourself on our &lt;a href=&quot;https://img.ly/showcases/cesdk/web/post-greeting-cards/web&quot;&gt;showcase&lt;/a&gt; page!&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://img.ly/use-cases/variable-data-printing&quot;&gt;Variable data printing&lt;/a&gt; can be a valuable tool to boost performance and take initiatives to the next level. It can help scale personalization efforts, acquire new customers, add value to the final product, and make employees feel appreciated. By using VDP, businesses can efficiently grow and reap numerous benefits, with only a few obstacles that can be easily overcome with the right solution.&lt;/p&gt;
&lt;p&gt;To take advantage of the benefits of VDP and overcome its challenges, consider using &lt;a href=&quot;https://img.ly/products/creative-sdk&quot;&gt;CreativeEditor SDK&lt;/a&gt; and see how it can help you scale personalization and bring variable data printing to your company and customers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! To stay in the loop, subscribe to our&lt;/strong&gt; &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;&lt;strong&gt;Newsletter&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Klaudia</dc:creator><media:content url="https://blog.img.ly/2023/03/VDP-print-product-web2print-web-application.jpg" medium="image"/><category>How-To</category><category>VDP</category><category>Automation</category><category>Creative Editor</category><category>Personlization</category><category>Web-to-print</category><category>Design Editor</category><category>Design Systems</category><category>Learning</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/showcases/cesdk/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>How to Apply Filters and Effects to an Image in Flutter</title><link>https://img.ly/blog/how-to-add-stickers-and-overlays-to-a-video-in-flutter-test/</link><guid isPermaLink="true">https://img.ly/blog/how-to-add-stickers-and-overlays-to-a-video-in-flutter-test/</guid><description>Learn how to manipulate images in Flutter with filters and effects.</description><pubDate>Tue, 15 Nov 2022 10:51:25 GMT</pubDate><content:encoded>&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Having previously discussed &lt;a href=&quot;https://img.ly/blog/how-to-resize-images-in-flutter/&quot;&gt;how Flutter deals with resizing&lt;/a&gt; and various overlays, in this tutorial, we will talk about a more popular type of image manipulation; applying filters and effects. At least since the ubiquity of social media platforms such as Instagram, Snapchat or more recently TikTok, applying various filters and effects has become an expected feature of any application dealing with image manipulation.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/flutter-apply-filters-to-video.mp4&quot; controls muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Applying Filters and Effects in Flutter demo application&lt;/p&gt;
&lt;p&gt;In this tutorial, we go through the process of applying filters and effects to images in Flutter. Like the example given in the video, we will examine the options for developing an image editor and the effects available in the Flutter framework. The &lt;a href=&quot;https://github.com/nataliakzm/Applying_Filters_and_Effects_to_Images_Flutter&quot;&gt;Git repository&lt;/a&gt; supporting this article can be cloned with the command provided below:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; clone&lt;/span&gt;&lt;span&gt; git@github.com:nataliakzm/Applying_Filters_and_Effects_to_Images_Flutter.git&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;flutter-imagefiltered-widget&quot;&gt;Flutter ImageFiltered Widget&lt;/h3&gt;
&lt;p&gt;One of the leading solutions proposed by the Flutter team on this issue was the &lt;code&gt;ImageFiltered&lt;/code&gt; &lt;a href=&quot;https://api.flutter.dev/flutter/widgets/ImageFiltered-class.html&quot;&gt;class&lt;/a&gt;. Overall, the widget deals with major image manipulations (from blurring to matrix transformation, rotation etc.), transforming and rearranging image pixels. To create an &lt;code&gt;ImageFilter&lt;/code&gt;, you need to follow a particular structure and apply the &lt;code&gt;ImageFilter&lt;/code&gt; class to its child elements:&lt;/p&gt;
&lt;p&gt;Moreover, in order to start working with &lt;code&gt;ImageFiltered&lt;/code&gt; within your Flutter application, first, don’t forget to import this library on the top of your .dart file.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; ImageFiltered&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  super&lt;/span&gt;&lt;span&gt;.key,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  required&lt;/span&gt;&lt;span&gt; this&lt;/span&gt;&lt;span&gt;.imageFilter,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  super&lt;/span&gt;&lt;span&gt;.child,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  this&lt;/span&gt;&lt;span&gt;.enabled &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&gt;&lt;span&gt; assert&lt;/span&gt;&lt;span&gt;(imageFilter &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;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &apos;dart:ui&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, &lt;code&gt;ImageFiltered&lt;/code&gt; offers several filter choices for image processing, depending on the specification of your app or visual asset.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;So, for example, with the help of &lt;code&gt;ImageFilter.blur&lt;/code&gt; or the so-called &lt;strong&gt;&lt;em&gt;Gaussian blur&lt;/em&gt;&lt;/strong&gt;, the pixels of the image can be blurred, which can, for instance, be used to create a graphic element that can serve as a background image.&lt;/li&gt;
&lt;li&gt;On the other hand, two other filters such as &lt;code&gt;ImageFilter.dilate&lt;/code&gt; and &lt;code&gt;ImageFilter.erode&lt;/code&gt; affect the pixels’ value by either enlarging them to the max significance within the specified range along the x and y axes or by shrinking pixels to their minimum values;&lt;/li&gt;
&lt;li&gt;You can also easily rotate, scale and change your asset in other ways by transforming its matrix with &lt;code&gt;ImageFilter.matrix&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Finally, &lt;code&gt;ImageFilter.compose&lt;/code&gt; is handy for merging two filters and combining their effects.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this point, the purpose of each effect seems much clearer, but we still miss some crucial steps to implement these filters in your Flutter application. Let’s see some real examples:&lt;/p&gt;
&lt;h3 id=&quot;imagefilterblur-constructor&quot;&gt;ImageFilter.blur constructor&lt;/h3&gt;
&lt;p&gt;To apply a blurring effect on your image, you have to call an ****&lt;code&gt;ImageFiltered.blur()&lt;/code&gt;&lt;strong&gt;,&lt;/strong&gt; and adjust the values of &lt;code&gt;sigmaX&lt;/code&gt; and &lt;code&gt;sigmaY&lt;/code&gt; arguments for blurring the image. Note that the asset must be called as a child element of the filter to achieve the desired effect.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Container&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;		child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Center&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;				child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Column&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;						mainAxisAlignment&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; MainAxisAlignment&lt;/span&gt;&lt;span&gt;.center,&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&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      // Blur an Image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      ImageFiltered&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;					imageFilter&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ImageFilter&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;blur&lt;/span&gt;&lt;span&gt;(sigmaY&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;span&gt;, sigmaX&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;					child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Image&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;asset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;assets/logo.png&quot;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;imagefiltermatrix-constructor&quot;&gt;ImageFilter.matrix constructor&lt;/h3&gt;
&lt;p&gt;The situation is slightly different with &lt;code&gt;ImageFilter.matrix&lt;/code&gt; filter since the effect can be initialized by one of two methods – &lt;code&gt;Float64List.fromList&lt;/code&gt; or &lt;code&gt;Matrix4.rotationZ&lt;/code&gt; . Thus, for the first method, the user needs to directly provide the matrix as a &lt;code&gt;Float64List&lt;/code&gt; as it is done below:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Matrix Transformation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ImageFiltered&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;		imageFilter&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ImageFilter&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;matrix&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Float64List&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fromList&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;			1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;			0.0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;			0.0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1.0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;			0.0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1.0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ])),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;		child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Image&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;asset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;assets/logo.png&quot;&lt;/span&gt;&lt;span&gt;, scale&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 3.5&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;// Note that scale is optional and depends on the image size&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While the &lt;code&gt;Matrix4&lt;/code&gt; class simply provides rotation in Z directions:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Matrix4 Rotation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ImageFiltered&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;		imageFilter&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ImageFilter&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;matrix&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Matrix4&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;rotationZ&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0.2&lt;/span&gt;&lt;span&gt;).storage),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;		child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Image&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;asset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;assets/logo.png&quot;&lt;/span&gt;&lt;span&gt;, scale&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 6&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;//Note that scale is optional and depends on the image size&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;colorfiltered-effects&quot;&gt;ColorFiltered Effects&lt;/h2&gt;
&lt;p&gt;Alternatively, Flutter has several options to integrate some filters without calling any extra library. One such example is the &lt;code&gt;ColorFilter&lt;/code&gt; function executed within the widget of the same name. &lt;code&gt;ColorFiltered&lt;/code&gt; processes two colors and outputs only one of them, which is eventually displayed at the top of the layer.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; // Black &amp;#x26; White Effect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ColorFiltered&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;		colorFilter&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; const&lt;/span&gt;&lt;span&gt; ColorFilter&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;mode&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Colors&lt;/span&gt;&lt;span&gt;.grey, &lt;/span&gt;&lt;span&gt;BlendMode&lt;/span&gt;&lt;span&gt;.saturation),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;		child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Image&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;asset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;assets/logo.png&quot;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Users can also engage with several construction methods: from directly calling the sRGB gamma curve with &lt;code&gt;ColorFilter.linearToSrgbGamma()&lt;/code&gt; widget or its inverse prototype with &lt;code&gt;ColorFilter.srgbToLinearGamma()&lt;/code&gt; to transforming the color by a 5x5 matrix within &lt;code&gt;ColorFilter.matrix()&lt;/code&gt; function or simply applying the blend mode with a given color specified in &lt;code&gt;ColorFilter.mode()&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;In conclusion, Flutter’s image processing capabilities are limited by a small variety of effects and often depend on the developer’s ability to handle complex concepts like matrix or sRGB curves. In this article, we reviewed some of the main image processing techniques proposed by the Flutter team and examined how to integrate them into photo editing apps. However, it is not always possible to allocate several days to calculate the correct matrix for transforming just one image when it comes to processing a large number of graphic assets. In such cases, you may want to think about using the &lt;a href=&quot;https://img.ly/products/photo-sdk&quot;&gt;PhotoEditor SDK&lt;/a&gt; for your next Flutter app.  Just like in the interactive example below, PhotoEditor SDK has many different effects and filters that will suit any of your requests.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/flutter-apply-filters-to-video2.mp4&quot; controls muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Applying Filters via PhotoEditor SDK&lt;/p&gt;
&lt;p&gt;To learn more, check out our article on &lt;a href=&quot;https://img.ly/docs/pesdk/flutter/getting-started/integration/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;PhotoEditor SDK and how to set it up in your app&lt;/a&gt; and &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;official PE.SDK guides&lt;/a&gt;. You can also follow our guide on how to integrate &lt;a href=&quot;https://img.ly/blog/a-modern-video-editor-sdk-for-your-flutter-app/&quot;&gt;video editor for Flutter&lt;/a&gt; into your app. For any of your questions that might occur in the process, you can contact our &lt;a href=&quot;https://img.ly/support?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;Support&lt;/a&gt;, who will be glad to help you.&lt;/p&gt;</content:encoded><dc:creator>Natalia</dc:creator><media:content url="https://blog.img.ly/2022/11/apply-filters-and-effects-to-an-image-in-Flutter.png" medium="image"/><category>How-To</category><category>Video Editing</category><category>Web Development</category><category>Flutter</category><category>Tech</category><category>Tutorial</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>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/use-cases/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>How To Resize Images in Flutter</title><link>https://img.ly/blog/how-to-resize-images-in-flutter/</link><guid isPermaLink="true">https://img.ly/blog/how-to-resize-images-in-flutter/</guid><description>Keep it light: resize your application images in Flutter. </description><pubDate>Tue, 27 Sep 2022 15:03:53 GMT</pubDate><content:encoded>&lt;p&gt;Static images are a core part of mobile applications. Usually, you store them in the directory in their original sizes, which requires you to adjust the sizes now and then, depending on where they are displayed. This article discusses how to resize images in Flutter and adjust their width, height, and size with efficient lines of code.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/flutter-resize-images.mp4&quot; controls muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h3 id=&quot;resizing-methods&quot;&gt;Resizing Methods&lt;/h3&gt;
&lt;p&gt;For image editing tools, Flutter offers an excellent and easy-in-use &lt;code&gt;Boxfit&lt;/code&gt; property that works within the &lt;code&gt;fit&lt;/code&gt; parameter, and you can easily integrate it into your application.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Image&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;asset&lt;/span&gt;&lt;span&gt;( &lt;/span&gt;&lt;span&gt;&apos;images/pexels.jpg&apos;&lt;/span&gt;&lt;span&gt;, fit&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; BoxFit&lt;/span&gt;&lt;span&gt;.cover)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Depending on your needs, you can choose between multiple attributes. For example, &lt;code&gt;.cover&lt;/code&gt; and &lt;code&gt;.fitHeight&lt;/code&gt; properties are similar when called, and both result in maximum frame coverage. Yet, although the image is widened proportionally, these methods can significantly affect the quality of crop image borders if they overfill. On the contrary, with &lt;code&gt;.fitWidth&lt;/code&gt; or &lt;code&gt;.scaledown&lt;/code&gt;, the asset is resized to the container’s width boundaries, which could be helpful when multiple images are needed to be displayed simultaneously.  If you are dealing with a static image of small size, you can also employ &lt;code&gt;.fill&lt;/code&gt; attribute that helps stretch your assets without cropping any critical information.&lt;/p&gt;
&lt;p&gt;Alternatively, by calling  .scale 0.5 the fit parameter will return you a graphical asset based on the scale you define. Note that any value less than 1 would reduce the image size.&lt;/p&gt;
&lt;h3 id=&quot;adjusting-image-size-with-flutter&quot;&gt;Adjusting Image Size with Flutter&lt;/h3&gt;
&lt;p&gt;Now, let’s look at how Flutter allows applying these image manipulation techniques. The crucial difference from the other frameworks is that in Flutter, images should be stored in a specific folder. In other words, once you upload graphic files, go to the &lt;code&gt;pubspec.yaml&lt;/code&gt; file and add the path to the directory around under &lt;code&gt;assets&lt;/code&gt;(&lt;strong&gt;Note:&lt;/strong&gt; by default the dependencies corresponding to ****&lt;code&gt;assets&lt;/code&gt;are usually placed around 45-46th lines in the code and need to be uncommented first by eliminating # and one space).&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Don&apos;t forget to define the assets folder in your directory&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# To do so, find lines 45 - 46 in the **pubspec.yaml** file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# and uncomment the assets section like this:&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&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  - &lt;/span&gt;&lt;span&gt;images/&lt;/span&gt;&lt;span&gt; #Note that this should correspond to the name of your folder&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatively, you can specify the path to each image in the list:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assets&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;assets/images/your_image.jpg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  - &lt;/span&gt;&lt;span&gt;assets/images/your_image2.jpg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the image is uploaded to Flutter, it seeks to occupy as much size as possible. But let’s presume that we have a “frame” or container that differs in size from the visual asset. One way to see the differences in sizes of both canvases is to color one frame, like color: Colors.indigo. Then we provide Flutter with the size specification and render our image in a child node child: Image.asset(‘images/pexels.jpg’). Thus, now we can see the image is placed inside the frame, and we need only to assign one of the filling methods (for example, fit: BoxFit.cover) discussed above.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &apos;package:flutter/cupertino.dart&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;package:flutter/material.dart&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;void&lt;/span&gt;&lt;span&gt; main&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  runApp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; ImgApp&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;class&lt;/span&gt;&lt;span&gt; ImgApp&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; StatelessWidget&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; ImgApp&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;span&gt;Key&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; key}) &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; super&lt;/span&gt;&lt;span&gt;(key&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; key);&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;  @override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Widget&lt;/span&gt;&lt;span&gt; build&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;BuildContext&lt;/span&gt;&lt;span&gt; context) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; MaterialApp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      debugShowCheckedModeBanner&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;      home&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; const&lt;/span&gt;&lt;span&gt; ResizePage&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 class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; ResizePage&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; StatelessWidget&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; ResizePage&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;span&gt;Key&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; key}) &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; super&lt;/span&gt;&lt;span&gt;(key&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; key);&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;  @override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Widget&lt;/span&gt;&lt;span&gt; build&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;BuildContext&lt;/span&gt;&lt;span&gt; context) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; Scaffold&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        appBar&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; AppBar&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          title&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Text&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;How to Resize Images&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          backgroundColor&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Color&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0xffe55586&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;        body&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Center&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            // Enabling the Image Frame&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Container&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Colors&lt;/span&gt;&lt;span&gt;.indigo, &lt;/span&gt;&lt;span&gt;// To see the difference between the image&apos;s original size and the frame&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 550&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                width&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 300&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;                // Uploading the Image from Assets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Image&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;asset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                  &apos;images/pexels.jpg&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;                  // Resizing the Image to the Frame Size&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                  fit&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; BoxFit&lt;/span&gt;&lt;span&gt;.cover,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                ))));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can find the associated to this tutorial Git repository ― &lt;a href=&quot;https://github.com/nataliakzm/Resizing_Images_with_Flutter&quot;&gt;here&lt;/a&gt;. Otherwise, run the following command to clone the complete code to your system:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; clone&lt;/span&gt;&lt;span&gt; &amp;#x3C;&lt;/span&gt;&lt;span&gt;https://github.com/nataliakzm/Resizing_Images_with_Flutte&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This article covered Flutter’s approach to the image resizing problem and saw how to use it in practice. However, despite the diversity of suggested methods that could fulfill basic users’ needs, Flutter fails to provide a comfortable integration. Just imagine you are building an app similar to Instagram in its functionality and you need not only upload various images from different assets but also resize them differently. If you want to avoid spending your time writing and maintaining a ton of code, then you may consider using &lt;a href=&quot;https://img.ly/products/photo-sdk?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;PhotoEditor SDK&lt;/a&gt; in your next project.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/photoeditor-sdk-resize-image.mp4&quot; controls muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;PhotoEditor SDK is available for various frameworks: first, read &lt;a href=&quot;https://img.ly/docs/pesdk/flutter/getting-started/integration/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;this article&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; to set up dependencies for your Flutter-based project. You can also follow our guide on how to integrate &lt;a href=&quot;https://img.ly/blog/a-modern-video-editor-sdk-for-your-flutter-app/&quot;&gt;video editor for Flutter&lt;/a&gt; into your app.&lt;/p&gt;
&lt;p&gt;In case you encounter any difficulties with the installation process, don’t hesitate to contact our &lt;a href=&quot;https://img.ly/support?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;support&lt;/a&gt; who will be happy to help you.  Then, as shown in the example below, you can efficiently resize, crop, rotate or flip your visual assets with the &lt;a href=&quot;https://img.ly/docs/pesdk/web/features/transform/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;transform tool&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Natalia</dc:creator><media:content url="https://blog.img.ly/2022/09/resize-images-in-flutter-1.png" medium="image"/><category>How-To</category><category>Flutter</category><category>Photo Editing</category><category>App Development</category><category>Framework</category><category>Tutorial</category></item><item><title>Comparing the Top 5 Open Source JavaScript Image Manipulation Libraries</title><link>https://img.ly/blog/the-top-5-open-source-javascript-image-manipulation-libraries/</link><guid isPermaLink="true">https://img.ly/blog/the-top-5-open-source-javascript-image-manipulation-libraries/</guid><description>Learn the difference between five major image manipulation libraries and choose the right one for you!</description><pubDate>Tue, 27 Sep 2022 13:29:03 GMT</pubDate><content:encoded>&lt;p&gt;Image Manipulation Libraries (IML) are used to perform various manipulation functions on images: you can increase the brightness of an image, add saturation or filters, crop and resize, and more valuable features that will help you to do almost everything and turn your web browser into an Adobe Lightroom!&lt;br&gt;
We have included libraries for every significant image manipulation task, from simple, low-level operations such as filters, brightness, and saturation to single-purpose libraries for cropping or image composition. To finish up, we wandered into the realm of testing images, since no image manipulation application is complete without a reliable way to test results.&lt;/p&gt;
&lt;h2 id=&quot;sharp&quot;&gt;Sharp&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.nodejsera.com/resize-image-using-sharp-nodejs.html#overview&quot;&gt;Sharp&lt;/a&gt; is a high-performance Node.js image processing library to resize different image formats such as JPEG, PNG, WebP, AVIF, SVG, and TIFF. The typical use case for this high-speed Node.js module is to convert large images in standard formats to smaller, web-friendly images.&lt;/p&gt;
&lt;p&gt;Sharp is helpful only if you want to resize a giant file or a variety of them. If, on the other hand, you only want to resize a single small image, then you probably shouldn’t use it. Instead, ordinary HTML and vanilla JavaScript will be more beneficial. Sharp is taking full advantage of multiple CPU cores and L1/L2/L3 cache, allowing you to resize and compress your images much more quickly.&lt;/p&gt;
&lt;h3 id=&quot;use-cases&quot;&gt;Use cases&lt;/h3&gt;
&lt;p&gt;I used it to resize a whole NFT Collection with a size of over 80Gb, which after compression came out at circa 10Gb. So again, if you would like to use it for resizing several large images, then it’s the best choice for you - you’ll do it in the fastest and most efficient way.&lt;/p&gt;
&lt;h3 id=&quot;example&quot;&gt;Example&lt;/h3&gt;
&lt;p&gt;You can install this library by using the command below:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; sharp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is how you would resize an image using sharp:&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;const&lt;/span&gt;&lt;span&gt; sharp&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;sharp&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; fs&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;fs&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;sharp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;yellow.png&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;rotate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;180&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;resize&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;200&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;toBuffer&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;then&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;data&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;    fs.&lt;/span&gt;&lt;span&gt;writeFileSync&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;yellow.png&apos;&lt;/span&gt;&lt;span&gt;, data);&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;catch&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;err&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;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(err);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;&quot;&gt;&lt;/h2&gt;
&lt;p&gt;Cropper.js&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/fengyuanchen/cropperjs&quot;&gt;Cropper.js&lt;/a&gt; is another popular JavaScript library for image manipulation. You can use it to crop your images in all possible ways, change aspect ratio, rotate, zoom and work with canvas data. &lt;a href=&quot;https://github.com/fengyuanchen/cropperjs&quot;&gt;Cropper.js&lt;/a&gt; is the right choice for cropping without any extra features.&lt;/p&gt;
&lt;h3 id=&quot;use-cases-1&quot;&gt;Use Cases&lt;/h3&gt;
&lt;p&gt;You can use its flexible API to create a custom image cropping UI in your web app allowing your users to adjust photos to the correct size and aspect ratio. It will be more efficient because it requires almost nothing, doesn’t have any useless features, and is very optimized for cropping!&lt;/p&gt;
&lt;h3 id=&quot;example-1&quot;&gt;Example&lt;/h3&gt;
&lt;p&gt;This library can be installed with the following command:&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;npm install cropperjs&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Cropper &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;cropperjs&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; image&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;image&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; cropper&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Cropper&lt;/span&gt;&lt;span&gt;(image, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  aspectRatio: &lt;/span&gt;&lt;span&gt;16&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 9&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  crop&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(event.detail.x);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(event.detail.y);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(event.detail.width);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(event.detail.height);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(event.detail.rotate);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(event.detail.scaleX);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(event.detail.scaleY);&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;Check out the Cropper.js &lt;a href=&quot;https://fengyuanchen.github.io/cropperjs/&quot;&gt;demo&lt;/a&gt;, click on any property that you want to apply to your image and see the result instantly.&lt;/p&gt;
&lt;p&gt;For an in-depth guide on how to add image cropping to your React app, check out our &lt;a href=&quot;https://img.ly/blog/how-to-crop-an-image-in-react-with-react-image-crop/&quot;&gt;guide on using&lt;/a&gt; &lt;a href=&quot;https://img.ly/blog/how-to-crop-an-image-in-react-with-react-image-crop/&quot;&gt;&lt;code&gt;react-image-crop&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;merge-images&quot;&gt;Merge Images&lt;/h2&gt;
&lt;p&gt;Working with the canvas can be slightly tedious, especially when you need the canvas context to do relatively simple things like merging a few images. &lt;a href=&quot;https://github.com/lukechilds/merge-images&quot;&gt;Merge Images&lt;/a&gt; abstracts all repetitive tasks into simple function calls. It’s a wrapper around the Canvas API, abstracting its low-level functions, which makes this specific task a lot easier. You can also create parameters like positioning, opacity, and several more. You can find them in the docs on &lt;a href=&quot;https://github.com/lukechilds/merge-images&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;use-cases-2&quot;&gt;Use cases&lt;/h3&gt;
&lt;p&gt;It’s a valuable library that can help with several tasks. For example, you can generate an NFT Collection where you can merge all components to have a complete set of variations. Or you can find this library helpful for personal use, for example, combining a few different images to generate a collage.&lt;/p&gt;
&lt;h3 id=&quot;example-2&quot;&gt;Example&lt;/h3&gt;
&lt;p&gt;Install the library itself:&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;npm install &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;save merge&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;images&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then you can use this code to generate a simple image:&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;//write this inside of your javascript file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; mergeImages &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;merge-images&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;mergeImages&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;&apos;/body.png&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;/eyes.png&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;/mouth.png&apos;&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  (&lt;/span&gt;&lt;span&gt;b64&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; (document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;img&apos;&lt;/span&gt;&lt;span&gt;).src &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; b64)&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;//And that would update the img element to show this image:&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, I used it to merge several components to create a final variation (here I used Background, Character, Horns, Emotion, and Accessories).&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Merge components for a new variation. I have used Background, Character, Horns, Emotion, and Accessories.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 4000px) 4000px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;4000&quot; height=&quot;5000&quot; src=&quot;https://img.ly/_astro/Cark--0454--1-_ZtQqaO.webp&quot; srcset=&quot;/_astro/Cark--0454--1-_iiCUQ.webp 640w, /_astro/Cark--0454--1-_Z4eEGA.webp 750w, /_astro/Cark--0454--1-_1JCKo3.webp 828w, /_astro/Cark--0454--1-_19SXG5.webp 1080w, /_astro/Cark--0454--1-_1esn2E.webp 1280w, /_astro/Cark--0454--1-_Z22yC93.webp 1668w, /_astro/Cark--0454--1-_2ab9uD.webp 2048w, /_astro/Cark--0454--1-_Z1QdBGV.webp 2560w, /_astro/Cark--0454--1-_ZtQqaO.webp 4000w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;lookssame&quot;&gt;LooksSame&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.npmjs.com/package/looks-same&quot;&gt;LooksSame&lt;/a&gt; is a library for comparing images. If the two images you uploaded are duplicates, the library will let you know. All you have to do is provide a link to the pictures you would like compared. LooksSame is not strictly an image manipulation library, but is helpful for testing purposes.&lt;/p&gt;
&lt;h3 id=&quot;use-cases-3&quot;&gt;Use cases&lt;/h3&gt;
&lt;p&gt;You can use this library and write code for automation and loop cycles that search a directory and compare each pair of your images for duplicates. Additionally, you can delete these duplicates and automate this process as well. It is a splendid library if you’re constantly working with ML algorithms that include large numbers of images.&lt;/p&gt;
&lt;h3 id=&quot;example-3&quot;&gt;Example&lt;/h3&gt;
&lt;p&gt;To start comparing images, you need just to install it by using this command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; i&lt;/span&gt;&lt;span&gt; looks-same&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, you can see how to test this library with &lt;a href=&quot;https://jestjs.io/docs/getting-started&quot;&gt;Jest&lt;/a&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;var&lt;/span&gt;&lt;span&gt; looksSame &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;looks-same&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;//Parameters can be paths to files or buffer with compressed png image&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;test&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;image1 and image2 are the same&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  expect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;looksSame&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;image1.png&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;image2.png&apos;&lt;/span&gt;&lt;span&gt;)).&lt;/span&gt;&lt;span&gt;toBe&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;//Result will be &quot;image1 and image2 are the same&quot; if they are the same&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;jimp&quot;&gt;Jimp&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jimp-dev/jimp&quot;&gt;Jimp&lt;/a&gt; stands for JavaScript Image Manipulation Program, that allows you to edit your image in almost every possible way. With it, you can invert your image, add some text, resize, use pixelation, clone images, blur them, invert colors, and several other cool features which will increase the image manipulation capabilities of your app.&lt;/p&gt;
&lt;h3 id=&quot;use-cases-4&quot;&gt;Use cases&lt;/h3&gt;
&lt;p&gt;With &lt;a href=&quot;https://github.com/jimp-dev/jimp&quot;&gt;Jimp&lt;/a&gt;, you can build your web application where you’ll be able to edit and manipulate images in almost any possible way. Crop, resize, rotate, and filter features let you create your own photo editor and add an interface on top of it. To streamline this process, you can try &lt;a href=&quot;https://img.ly/products/photo-sdk&quot;&gt;&lt;strong&gt;PhotoEditor SDK&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;(PE.SDK)&lt;/strong&gt;. PE.SDK will provide you with a polished user interface and photo editing features, so you can focus on the application you are building instead.&lt;/p&gt;
&lt;h3 id=&quot;example-4&quot;&gt;Example&lt;/h3&gt;
&lt;p&gt;Here’s the JavaScript code to try this excellent library. There’s an async function in which you can see many types of properties that you can easily change. Such as &lt;em&gt;Add Text&lt;/em&gt;, &lt;em&gt;Resize Image&lt;/em&gt;, &lt;em&gt;Blur the Image&lt;/em&gt;, etc. To use at least one of them, you’ll need to uncomment only part that you need, and your program is ready to run!&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 dependencies&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; Jimp&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;jimp&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;(&lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; function&lt;/span&gt;&lt;span&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // Read the image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; image&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; Jimp.&lt;/span&gt;&lt;span&gt;read&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;images/shapes.png&apos;&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;// &amp;#x3C;http://www.example.com/path/to/lenna.jpg&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;  // // Add text&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  const font = await Jimp.loadFont(Jimp.FONT_SANS_16_WHITE); // bitmap fonts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  image.print(font, 0, 0, &apos;Hello world!&apos;); // &amp;#x3C;https://github.com/libgdx/libgdx/wiki/Hiero&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;  //  // Resize the image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  // Resize the image to 250 x 250&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  image.resize(250, 250);&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;  //  // Resize the height to 250 and scale the width accordingly&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  image.resize(Jimp.AUTO, 250);&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;  //  // Resize the width to 250 and scale the height accordingly&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  image.resize(250, Jimp.AUTO);&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;  //  // Add a sepia wash to the image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  image.sepia();&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;  //  // Pixelation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  image.pixelate(5);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  image.pixelate(5, 50, 50, 190, 200); pixe,x, y, w, h&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;  //  // Clone&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  const image2 = image.clone();&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;  //  // Blur the image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  image.gaussian(1);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  image.blur(1);&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;  //  // Inverts the image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  image.invert();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  // Set the brightness&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  image.brightness( 0.5 ); // -1 to +1&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;  //  // Resize the image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  image.resize(256, 256);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  // Set the quality&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  image.quality(100);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  // Convert to grayscale&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //  image.greyscale();&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;  // Save the image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  image.&lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;images/edited-shapes.png&apos;&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;// writeAsync&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;PS: Don’t forget to uncomment the effects which you want to use and save the file after. :)&lt;/p&gt;
&lt;p&gt;As you can see here, I blurred everything and inverted the colors for the previous image that I showed you, getting the following result:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2000px) 2000px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2000&quot; height=&quot;2500&quot; src=&quot;https://img.ly/_astro/edited-shapes--1-_ZYNNbY.webp&quot; srcset=&quot;/_astro/edited-shapes--1-_Z1zlAHs.webp 640w, /_astro/edited-shapes--1-_rXLqj.webp 750w, /_astro/edited-shapes--1-_JK5t4.webp 828w, /_astro/edited-shapes--1-_ZfCEJE.webp 1080w, /_astro/edited-shapes--1-_Z1bDJEA.webp 1280w, /_astro/edited-shapes--1-_ZI62ME.webp 1668w, /_astro/edited-shapes--1-_ZYNNbY.webp 2000w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;That’s it! Now you know the differences between five of the most popular image manipulation libraries for JavaScript. Some of them are useful for only one task, such as resizing images - others can do multiple tasks at once and help you with more processes. You have also met &lt;a href=&quot;https://img.ly/products/photo-sdk&quot;&gt;PE.SDK&lt;/a&gt; to work smarter and save time building photo editing solutions, and focus on building your great product instead.&lt;/p&gt;</content:encoded><dc:creator>Mark</dc:creator><media:content url="https://blog.img.ly/2022/09/image-manipulation-libraries-javascript-1.png" medium="image"/><category>How-To</category><category>Image Manipulation</category><category>Image Editing</category><category>Insights</category></item><item><title>How to Crop and Trim Videos in Flutter</title><link>https://img.ly/blog/how-to-crop-and-trim-videos-in-flutter/</link><guid isPermaLink="true">https://img.ly/blog/how-to-crop-and-trim-videos-in-flutter/</guid><description>Create your own video app in Flutter with the free-to-use and open source solution FFmpeg.</description><pubDate>Tue, 27 Sep 2022 08:45:16 GMT</pubDate><content:encoded>&lt;p&gt;If you are looking for a package that crops and trims videos in Flutter, you must have already come across the &lt;a href=&quot;https://pub.dev/packages/video_trimmer&quot;&gt;video_trimmer&lt;/a&gt; Flutter package. This package can trim videos but does not provide video cropping (at least not out-of-the-box). In fact, none of the packages on pub.dev, as of today, allow cropping a video in Flutter. If you have dug deeper, you might have come across FFmpeg — a powerful video editing command line tool, that is not the easiest to get started with. See the &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg/&quot;&gt;Ultimate Guide to FFmpeg&lt;/a&gt; for help.In this article we will use the FFmpeg library to crop and trim a video in a Flutter project.&lt;/p&gt;
&lt;p&gt;Here is a list of packages that we will be using; &lt;a href=&quot;https://pub.dev/packages/ffmpeg_kit_flutter&quot;&gt;ffmpeg_kit_flutter&lt;/a&gt; package for cropping and trimming videos, the &lt;a href=&quot;https://pub.dev/packages/path_provider&quot;&gt;path_provider&lt;/a&gt; package to get the path to the application or external directory where the video files will be stored, and the &lt;a href=&quot;https://pub.dev/packages/video_player&quot;&gt;video_player&lt;/a&gt; package to play the video preview.&lt;/p&gt;
&lt;p&gt;This article will not cover building any UI for cropping and trimming. But, it will discuss in brief how to crop the video using the video_trimmer library as well. If you want to package this workflow for production, check out our guide on &lt;a href=&quot;https://img.ly/blog/how-to-run-ffmpeg-inside-a-docker-container/&quot;&gt;running FFmpeg inside a Docker container&lt;/a&gt;. Also, you can try out the code used in this article from this &lt;a href=&quot;https://github.com/numerative/flutter_crop_and_trim_video&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;get-started&quot;&gt;Get Started&lt;/h2&gt;
&lt;p&gt;The end result of this tutorial will be a simple app that would look like the following screenshot.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Create a Flutter app to trim and crop videos easily.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 323px) 323px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;323&quot; height=&quot;700&quot; src=&quot;https://img.ly/_astro/resized_Z1EVmRl.webp&quot; srcset=&quot;/_astro/resized_Z1EVmRl.webp 323w&quot;&gt;&lt;/p&gt;
&lt;p&gt;When you tap the &lt;strong&gt;Save Video&lt;/strong&gt;, the preview will refresh with the cropped and trimmed video replacing the original video. Follow the instructions in this article, and you will be able to develop a similar Flutter app.&lt;/p&gt;
&lt;p&gt;First, let us start by adding dependencies to a new Flutter project.&lt;/p&gt;
&lt;h3 id=&quot;add-dependencies&quot;&gt;Add Dependencies&lt;/h3&gt;
&lt;p&gt;In your project’s &lt;code&gt;pubspec.yaml&lt;/code&gt; file add the following 3 dependencies.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dependencies&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_kit_flutter&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;^4.5.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  path_provider&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;^2.0.11&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  video_player&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;^2.4.6&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, execute &lt;code&gt;flutter pub get&lt;/code&gt; command from the project folder root.&lt;/p&gt;
&lt;h3 id=&quot;set-minimum-sdk-version-and-platform-version&quot;&gt;Set Minimum SDK Version and Platform Version&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;ffmpeg_kit_flutter&lt;/code&gt; plugin runs on Android SDK API level 24+ and iOS SDK 12.1+. Therefore, modify the module level &lt;code&gt;build.gradle&lt;/code&gt; file to declare the &lt;code&gt;minSdkVersion&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;groovy&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;android {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  defaultConfig {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ..&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    minSdkVersion &lt;/span&gt;&lt;span&gt;24&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 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;Then, modify the &lt;code&gt;Podfile&lt;/code&gt; to declare the minimum global platform.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Uncomment this line to define a global platform for your project&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;platform &lt;/span&gt;&lt;span&gt;:ios&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;12.1&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;add-a-video-asset&quot;&gt;Add a Video Asset&lt;/h3&gt;
&lt;p&gt;For simplicity, we will be using a video asset instead of implementing a file picker. Choose a video file you would like to work with or &lt;a href=&quot;https://github.com/numerative/flutter_crop_and_trim_video/raw/main/assets/file1.mp4&quot;&gt;download&lt;/a&gt; this sample video file. Next, copy the video file to a new directory named &lt;code&gt;assets&lt;/code&gt; at the root of your project folder.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Copy your video of choice to your assets folder at the root of your project folder.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 318px) 318px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;318&quot; height=&quot;240&quot; src=&quot;https://img.ly/_astro/file1-mp4-placement_ZVFQb9.webp&quot; srcset=&quot;/_astro/file1-mp4-placement_ZVFQb9.webp 318w&quot;&gt;&lt;/p&gt;
&lt;p&gt;And then reference the video file from the &lt;code&gt;pubspec.yaml&lt;/code&gt; file.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;flutter&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&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    - &lt;/span&gt;&lt;span&gt;assets/file1.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Execute &lt;code&gt;flutter pub get&lt;/code&gt; again.&lt;/p&gt;
&lt;p&gt;Having added the required dependencies, let us move on to coding.&lt;/p&gt;
&lt;h3 id=&quot;implement-video-crop-and-trim&quot;&gt;Implement Video Crop and Trim&lt;/h3&gt;
&lt;p&gt;Replace the code in your &lt;code&gt;main.dart&lt;/code&gt; file with the following code in the code block. This will be our starter code.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &apos;package:flutter/material.dart&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;void&lt;/span&gt;&lt;span&gt; main&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  runApp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; MyApp&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;class&lt;/span&gt;&lt;span&gt; MyApp&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; StatelessWidget&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; MyApp&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;span&gt;Key&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; key}) &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; super&lt;/span&gt;&lt;span&gt;(key&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; key);&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;  // This widget is the root of your application.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  @override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Widget&lt;/span&gt;&lt;span&gt; build&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;BuildContext&lt;/span&gt;&lt;span&gt; context) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; MaterialApp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      title&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &apos;Crop and Trim Demo&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      home&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; const&lt;/span&gt;&lt;span&gt; MyHomePage&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 class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; MyHomePage&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; StatefulWidget&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; MyHomePage&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;span&gt;Key&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; key}) &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; super&lt;/span&gt;&lt;span&gt;(key&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; key);&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;  @override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  State&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;MyHomePage&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;createState&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; _MyHomePageState&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;class&lt;/span&gt;&lt;span&gt; _MyHomePageState&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; State&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;MyHomePage&lt;/span&gt;&lt;span&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  @override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Widget&lt;/span&gt;&lt;span&gt; build&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;BuildContext&lt;/span&gt;&lt;span&gt; context) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; Scaffold&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      appBar&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; AppBar&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        title&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Text&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Flutter Crop and Trim&quot;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      body&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Column&lt;/span&gt;&lt;span&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&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;initialize-the-input-player&quot;&gt;Initialize the Input Player&lt;/h3&gt;
&lt;p&gt;Add the following code to the &lt;code&gt;_MyHomePageState&lt;/code&gt; class.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &apos;dart:io&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;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &apos;package:flutter/services.dart&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;package:path_provider/path_provider.dart&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;package:video_player/video_player.dart&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;...&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;class&lt;/span&gt;&lt;span&gt; _MyHomePageState&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; State&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;MyHomePage&lt;/span&gt;&lt;span&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  late&lt;/span&gt;&lt;span&gt; String&lt;/span&gt;&lt;span&gt; inputPath;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  VideoPlayerController&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; controller;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  String&lt;/span&gt;&lt;span&gt; outputPath &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &quot;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  @override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  void&lt;/span&gt;&lt;span&gt; initState&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;initState&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    copyVideoToApplicationDirectory&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt;((path) &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      inputPath &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; path;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      controller &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; VideoPlayerController&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;File&lt;/span&gt;&lt;span&gt;(inputPath));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      await&lt;/span&gt;&lt;span&gt; controller&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;initialize&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      await&lt;/span&gt;&lt;span&gt; controller&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;play&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      setState&lt;/span&gt;&lt;span&gt;(() {});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      outputPath &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; getOutputPath&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;  ///Copy input file to ApplicationStorage Directory&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ///returns path to copied video&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Future&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;copyVideoToApplicationDirectory&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; filename &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &quot;file1.mp4&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    var&lt;/span&gt;&lt;span&gt; bytes &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; rootBundle.&lt;/span&gt;&lt;span&gt;load&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;assets/file1.mp4&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    String&lt;/span&gt;&lt;span&gt; dir &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; getApplicationDocumentsDirectory&lt;/span&gt;&lt;span&gt;()).path;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    writeToFile&lt;/span&gt;&lt;span&gt;(bytes, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;dir&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;filename&lt;/span&gt;&lt;span&gt;&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;    return&lt;/span&gt;&lt;span&gt; &apos;&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;dir&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;filename&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ///Write to Path.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Future&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;writeToFile&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ByteData&lt;/span&gt;&lt;span&gt; data, &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; path) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    final&lt;/span&gt;&lt;span&gt; buffer &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; data.buffer;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; File&lt;/span&gt;&lt;span&gt;(path).&lt;/span&gt;&lt;span&gt;writeAsBytes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        buffer.&lt;/span&gt;&lt;span&gt;asUint8List&lt;/span&gt;&lt;span&gt;(data.offsetInBytes, data.lengthInBytes));&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;The &lt;code&gt;copyVideoToApplicationDirectory&lt;/code&gt; method is copying the video from Assets to the Application directory on the phone’s file system. The resulting path is then stored in the &lt;code&gt;inputPath&lt;/code&gt; variable which is then supplied to the FFmpeg command. For large‑scale workloads, you can move this FFmpeg command to &lt;a href=&quot;https://img.ly/blog/how-to-run-ffmpeg-on-aws-spot-instances-for-scalable-low-cost-video-processing/&quot;&gt;AWS Spot Instances&lt;/a&gt;; our AWS guide shows how to configure a cloud environment for scalable video processing.&lt;/p&gt;
&lt;p&gt;Next, add the &lt;code&gt;VideoPlayer&lt;/code&gt; widget to the &lt;code&gt;Column&lt;/code&gt; widget.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Column&lt;/span&gt;&lt;span&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&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    (controller &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&gt;&lt;span&gt; AspectRatio&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        aspectRatio&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; controller&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;.value.aspectRatio,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; VideoPlayer&lt;/span&gt;&lt;span&gt;(controller&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; const&lt;/span&gt;&lt;span&gt; SizedBox&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the app and the video should start playing.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;You can now play your video.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 323px) 323px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;323&quot; height=&quot;700&quot; src=&quot;https://img.ly/_astro/resized_screen_2sxrDY.webp&quot; srcset=&quot;/_astro/resized_screen_2sxrDY.webp 323w&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the next step, we will add the FFmpeg command, but before that add the following 2 more methods to the &lt;code&gt;_MyHomePageState&lt;/code&gt; class.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&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;import&lt;/span&gt;&lt;span&gt; &apos;package:ffmpeg_kit_flutter/ffmpeg_kit.dart&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;package:ffmpeg_kit_flutter/log.dart&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;package:ffmpeg_kit_flutter/return_code.dart&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;...&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;class&lt;/span&gt;&lt;span&gt; _MyHomePageState&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; State&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;MyHomePage&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;  /// Output path with a file name where the result will be stored.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Future&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;getOutputPath&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    final&lt;/span&gt;&lt;span&gt; appDirectory &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Platform&lt;/span&gt;&lt;span&gt;.isAndroid&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        ?&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; getExternalStorageDirectory&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; await&lt;/span&gt;&lt;span&gt; getApplicationDocumentsDirectory&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    final&lt;/span&gt;&lt;span&gt; externalPath &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;appDirectory&lt;/span&gt;&lt;span&gt;?.&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/out_file.mp4&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; externalPath;&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;  ///Executes the FFMPEG &lt;/span&gt;&lt;span&gt;[command]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ///Note: Green bar on the right is a Flutter issue. &amp;#x3C;https://github.com/flutter/engine/pull/24888&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ///Should get fixed in a 3.1.0+ stable release &amp;#x3C;https://github.com/flutter/engine/pull/24888#issuecomment-1212374010&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Future&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;ffmpegExecute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; command) &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    final&lt;/span&gt;&lt;span&gt; session &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; FFmpegKit&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;execute&lt;/span&gt;&lt;span&gt;(command);&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;    final&lt;/span&gt;&lt;span&gt; returnCode &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; session.&lt;/span&gt;&lt;span&gt;getReturnCode&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;ReturnCode&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;isSuccess&lt;/span&gt;&lt;span&gt;(returnCode)) {&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;      print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Success&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      //Replace the preview video&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      await&lt;/span&gt;&lt;span&gt; controller&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;pause&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      await&lt;/span&gt;&lt;span&gt; controller&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&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;      controller &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; VideoPlayerController&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;File&lt;/span&gt;&lt;span&gt;(outputPath));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      await&lt;/span&gt;&lt;span&gt; controller&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;initialize&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      await&lt;/span&gt;&lt;span&gt; controller&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;play&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      setState&lt;/span&gt;&lt;span&gt;(() {});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;ReturnCode&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;isCancel&lt;/span&gt;&lt;span&gt;(returnCode)) {&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;      print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Cancel&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Error&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      final&lt;/span&gt;&lt;span&gt; failStackTrace &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; session.&lt;/span&gt;&lt;span&gt;getFailStackTrace&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      print&lt;/span&gt;&lt;span&gt;(failStackTrace);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      List&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Log&lt;/span&gt;&lt;span&gt;&gt; logs &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; session.&lt;/span&gt;&lt;span&gt;getLogs&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; element &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; logs) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        print&lt;/span&gt;&lt;span&gt;(element.&lt;/span&gt;&lt;span&gt;getMessage&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;    }&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;The &lt;code&gt;getOutputPath&lt;/code&gt; method provides the path where the resulting video will be saved. The &lt;code&gt;outputPath&lt;/code&gt; will be passed to the FFmpeg command whereas the &lt;code&gt;ffmpegExecute&lt;/code&gt; method is where the FFmpeg magic takes place.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ffmpegExecute&lt;/code&gt; method expects a valid FFmpeg &lt;code&gt;String&lt;/code&gt; command. The &lt;code&gt;String&lt;/code&gt; command is then passed to the &lt;code&gt;FFmpegKit.execute&lt;/code&gt; method which returns an instance of &lt;code&gt;FFMpegSession&lt;/code&gt; . It will tell us whether our command was executed successfully or not. If it was not executed successfully, we can extract error logs from it.&lt;/p&gt;
&lt;h3 id=&quot;ffmpeg-execute-command&quot;&gt;FFmpeg Execute Command&lt;/h3&gt;
&lt;p&gt;Add a &lt;code&gt;TextButton&lt;/code&gt; from where the FFmpeg command shall be sent.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;TextButton&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Text&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Save Video&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  onPressed&lt;/span&gt;&lt;span&gt;:&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //TODO: Call FFMPEG Execute&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;Next, call the following &lt;code&gt;ffmpegExecute&lt;/code&gt; method from the &lt;code&gt;onPressed&lt;/code&gt; property.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpegExecute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;-ss 0:00:15 -to 0:00:45 -y -i &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;inputPath&lt;/span&gt;&lt;span&gt; -filter:v &quot;crop=320:150&quot; -c:a copy &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;outputPath&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the app and tap the &lt;strong&gt;Save Video&lt;/strong&gt; button. You will notice that the player is now playing the new cropped and trimmed video.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;You can now play your cropped and trimmed video!&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 323px) 323px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;323&quot; height=&quot;700&quot; src=&quot;https://img.ly/_astro/flutter_video_app_crop_trim_screen_ZP5gb4.webp&quot; srcset=&quot;/_astro/flutter_video_app_crop_trim_screen_ZP5gb4.webp 323w&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you were able to successfully follow the instructions up till here, it is time to dig a little deeper into the FFmpeg command that we just ran earlier.&lt;/p&gt;
&lt;h3 id=&quot;understanding-the-ffmpeg-command&quot;&gt;Understanding the FFmpeg Command&lt;/h3&gt;
&lt;p&gt;Because command FFmpeg is a command line tool, it expects string-only commands. For this reason, we do not have any dart Classes, Methods, or Parameters to work with that we usually get when working with a dart plugin. Let us dissect the above command line command that is helping us crop and trim a video.&lt;/p&gt;





































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Command&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;-ss 0:00:15&lt;/td&gt;&lt;td&gt;Seeks to position on the input video. The trim starts from this position.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-to 0:00:45&lt;/td&gt;&lt;td&gt;Stops reading at the position in the input video. The trim stops at this position. The total length of the video is 0:02:05.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-y&lt;/td&gt;&lt;td&gt;Overwrite output files. Helpful when the Save Video button is tapped more than once.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-i $inputPath&lt;/td&gt;&lt;td&gt;Input file location. This is the file on which the crop and trim are applied.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-filter:v&lt;/td&gt;&lt;td&gt;Apply a filter to the video stream.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;”crop=320:150”&lt;/td&gt;&lt;td&gt;Apply a crop filter from the center of the video that is 320 pixels wide and 150 pixels tall. The original dimensions of the video were 320 x 240.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-c:a copy $outputPath&lt;/td&gt;&lt;td&gt;Specifies the codec with which the output file must be encoded. Here, copy is a special value to indicate the stream is not to be re-encoded. The a after the colon is a stream specifier for the audio stream.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;That will be either the application directory or the external directory of the application, depending on whether the app is running on iOS or Android.&lt;/p&gt;
&lt;p&gt;You can check out the app’s code and play with it by downloading it from this &lt;a href=&quot;https://github.com/numerative/flutter_crop_and_trim_video&quot;&gt;GitHub Repository&lt;/a&gt;. This project allows you to tweak the duration of the trim and dimensions of the crop, so it has more code, but at the heart of it, it still uses the above FFmpeg command.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Download this app code from the GitHub Repository.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 378px) 378px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;378&quot; height=&quot;700&quot; src=&quot;https://img.ly/_astro/flutter_video_app_crop_trim_info_Z2pzU1G.webp&quot; srcset=&quot;/_astro/flutter_video_app_crop_trim_info_Z2pzU1G.webp 378w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;cropping-using-the-video_trimmer-plugin&quot;&gt;Cropping Using the video_trimmer Plugin&lt;/h2&gt;
&lt;p&gt;As a bonus, here is a tip on how to use the video_trimmer plugin for cropping videos.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://pub.dev/packages/video_trimmer&quot;&gt;video_trimmer&lt;/a&gt; plugin also uses FFmpeg at its core and allows us to pass on an FFmpeg command while saving the video. For this reason, it is an easy task for us to apply the crop to a video using the video_trimmer plugin.&lt;/p&gt;
&lt;p&gt;To do this, pass the following command to the &lt;code&gt;saveTrimmedVideo&lt;/code&gt; method’s &lt;code&gt;ffmpegCommand&lt;/code&gt; parameter as shown in the code block below. The video_trimmer package expects the &lt;code&gt;customVideoFormat&lt;/code&gt; parameter argument when the &lt;code&gt;ffmpegCommand&lt;/code&gt; parameter is used.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;await &lt;/span&gt;&lt;span&gt;_trimmer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;saveTrimmedVideo&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        startValue:&lt;/span&gt;&lt;span&gt; _startValue&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        endValue:&lt;/span&gt;&lt;span&gt; _endValue&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        ffmpegCommand:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            &apos;-filter:v &quot;crop=320:150&quot;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        customVideoFormat:&lt;/span&gt;&lt;span&gt; &apos;.mp4&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;then&lt;/span&gt;&lt;span&gt;((value) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  setState&lt;/span&gt;&lt;span&gt;(() {&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; value;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;limitations-of-this-approach&quot;&gt;Limitations of this Approach&lt;/h2&gt;
&lt;p&gt;The crop that we are applying is from the center of the frame. The present command would also need starting coordinates to crop a non-center frame.&lt;/p&gt;
&lt;p&gt;The project will need a refined UI for cropping and trimming videos to offer a complete app experience to users. The current approach is miles away from creating that experience.&lt;/p&gt;
&lt;p&gt;But perhaps the most significant limitation is FFmpeg’s &lt;strong&gt;licensing&lt;/strong&gt;. FFmpeg is available with both LGPL and GPL licenses, so you must ensure your project is compatible with those licenses. For many commercial projects, this is a non-starter. Alternatively, you can &lt;a href=&quot;https://img.ly/blog/ffmpeg-on-google-cloud-platform-guide/&quot;&gt;run FFmpeg on Google Cloud Platform&lt;/a&gt;; our GCP tutorial walks you through setup.&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 (VE.SDK)&lt;/a&gt; from &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 a Flutter project. You will receive staples of video editing, including straightening videos, filters, brightness, color adjustments, and more. Follow our guide to learn how to integrate IMG.LY’s &lt;a href=&quot;https://img.ly/blog/a-modern-video-editor-sdk-for-your-flutter-app/&quot;&gt;video editor for Flutter&lt;/a&gt; into your app.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;While still a complex topic, video manipulation is more attainable to implement on Flutter than on native Android. Nevertheless, FFmpeg is the only open-source, free-to-use option to edit videos on Flutter right now. To automate cropping and trimming across many files or build a transcoding server, see our article on &lt;a href=&quot;https://img.ly/blog/building-a-production-ready-batch-video-processing-server-with-ffmpeg/&quot;&gt;building a production‑ready batch video processing server.&lt;/a&gt;&lt;/p&gt;</content:encoded><dc:creator>Michael H.</dc:creator><media:content url="https://blog.img.ly/2022/09/trim_videos_with_flutter_tutorial.png" medium="image"/><category>How-To</category><category>Flutter</category><category>Video Editor</category><category>App Development</category><category>FFmpeg</category><category>Tutorial</category></item></channel></rss>