<?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>Video Editing – IMG.LY Blog</title><description>Posts tagged Video Editing on the IMG.LY blog.</description><link>https://img.ly/blog/tag/video-editing/</link><language>en-us</language><image><url>https://img.ly/apple-touch-icon.png</url><title>Video Editing – IMG.LY Blog</title><link>https://img.ly/blog/tag/video-editing/</link></image><atom:link href="https://img.ly/blog/tag/video-editing/rss.xml" rel="self" type="application/rss+xml"/><generator>Astro</generator><lastBuildDate>Tue, 09 Jun 2026 09:48:32 GMT</lastBuildDate><ttl>60</ttl><item><title>Build in a Day: AI Video Clipping with CE.SDK</title><link>https://img.ly/blog/build-in-a-day-ai-video-clipping-with-ce-sdk/</link><guid isPermaLink="true">https://img.ly/blog/build-in-a-day-ai-video-clipping-with-ce-sdk/</guid><description>Build a client-side AI video editor that turns long videos into short highlights using CE.SDK and AI.</description><pubDate>Thu, 05 Feb 2026 12:17:28 GMT</pubDate><content:encoded>&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;We built a video shortener in a single day using Claude Code and CE.SDK. It extracts 3-4 short clips from long-form video, handles transcription, identifies the best moments via AI, detects speakers, and outputs vertical/horizontal/square formats—all running in the browser.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Features:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Extracts 3-4 clips per video (highlights, summaries, or cleaned-up edits)&lt;/li&gt;
&lt;li&gt;Outputs 9:16 (vertical), 16:9 (landscape), or 1:1 (square)&lt;/li&gt;
&lt;li&gt;Detects speakers and maps them to faces with user confirmation&lt;/li&gt;
&lt;li&gt;Auto-crops to follow the active speaker&lt;/li&gt;
&lt;li&gt;Adds captions and text hooks&lt;/li&gt;
&lt;li&gt;Non-destructive: change aspect ratio or template without re-processing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Best suited for:&lt;/strong&gt; Videos with speech/dialogue (podcasts, interviews, presentations, vlogs)&lt;/p&gt;
&lt;h2 id=&quot;why-client-side&quot;&gt;Why Client-Side?&lt;/h2&gt;
&lt;p&gt;CE.SDK’s CreativeEngine runs in the browser via WebAssembly. Video decoding, timeline manipulation, effects, and preview all happen on the user’s device.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Benefits:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No upload/download wait — edits preview instantly&lt;/li&gt;
&lt;li&gt;Non-destructive — switch aspect ratio or template without rendering&lt;/li&gt;
&lt;li&gt;Lower infrastructure costs — your costs don’t scale with video length or user count&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tech-stack&quot;&gt;Tech Stack&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Frontend:&lt;/strong&gt; Next.js + React&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Engine:&lt;/strong&gt; CE.SDK (CreativeEngine)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transcription:&lt;/strong&gt; ElevenLabs Scribe v2&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI Analysis:&lt;/strong&gt; Google Gemini&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;architecture-overview&quot;&gt;Architecture Overview&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;High-Level Flow&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;AI Video Shortener&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 818px) 818px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;818&quot; height=&quot;841&quot; src=&quot;https://img.ly/_astro/ai-video-processing-build-a-video-shortener_20dJmS.webp&quot; srcset=&quot;/_astro/ai-video-processing-build-a-video-shortener_1wQnGV.webp 640w, /_astro/ai-video-processing-build-a-video-shortener_Z2vwzkB.webp 750w, /_astro/ai-video-processing-build-a-video-shortener_20dJmS.webp 818w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Required API Keys&lt;/strong&gt;&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Service&lt;/th&gt;&lt;th&gt;Purpose&lt;/th&gt;&lt;th&gt;Environment Variable&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;CE.SDK&lt;/td&gt;&lt;td&gt;Video editing engine&lt;/td&gt;&lt;td&gt;&lt;code&gt;NEXT_PUBLIC_CESDK_LICENSE&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ElevenLabs&lt;/td&gt;&lt;td&gt;Speech-to-text transcription&lt;/td&gt;&lt;td&gt;&lt;code&gt;ELEVENLABS_API_KEY&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Gemini (via OpenRouter or direct)&lt;/td&gt;&lt;td&gt;AI highlight detection&lt;/td&gt;&lt;td&gt;&lt;code&gt;OPENROUTER_API_KEY&lt;/code&gt; or &lt;code&gt;GEMINI_API_KEY&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;setting-up-cesdk&quot;&gt;Setting Up CE.SDK&lt;/h2&gt;
&lt;h3 id=&quot;what-is-cesdk&quot;&gt;What is CE.SDK?&lt;/h3&gt;
&lt;p&gt;CE.SDK (CreativeEngine SDK) is a browser-based engine for video, image, and design editing—a programmable video editor you can embed in your app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Concepts:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Engine:&lt;/strong&gt; The runtime that manages the editing session&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scene:&lt;/strong&gt; The document/project containing all elements&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blocks:&lt;/strong&gt; Individual elements (video clips, text, shapes, audio)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Timeline:&lt;/strong&gt; Time-based arrangement of blocks for video editing&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;installation&quot;&gt;Installation&lt;/h3&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;h3 id=&quot;initializing-the-creativeengine&quot;&gt;Initializing the CreativeEngine&lt;/h3&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; CreativeEngine &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;const&lt;/span&gt;&lt;span&gt; engine&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; CreativeEngine.&lt;/span&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;  license: process.env.&lt;/span&gt;&lt;span&gt;NEXT_PUBLIC_CESDK_LICENSE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Create a video scene&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; scene&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.scene.&lt;/span&gt;&lt;span&gt;createVideo&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;// Get the page (timeline container)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; pages&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.scene.&lt;/span&gt;&lt;span&gt;getPages&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; page&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; pages[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Configure page dimensions for your target aspect ratio&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;(page, &lt;/span&gt;&lt;span&gt;1080&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;// 9:16 vertical&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;(page, &lt;/span&gt;&lt;span&gt;1920&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;uploading-video-to-cesdk&quot;&gt;Uploading Video to CE.SDK&lt;/h3&gt;
&lt;p&gt;CE.SDK works with video through a fill-based system. The graphic block is the container, while the video fill holds the actual media source and playback properties.&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;// Create a video block&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; videoBlock&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;graphic&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; videoFill&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;createFill&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;video&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;// Set the video source&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.&lt;/span&gt;&lt;span&gt;setString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  videoFill,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;fill/video/fileURI&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  videoUrl &lt;/span&gt;&lt;span&gt;// Can be a blob URL or remote URL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;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;// Apply fill to 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;setFill&lt;/span&gt;&lt;span&gt;(videoBlock, videoFill);&lt;/span&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 to timeline&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.&lt;/span&gt;&lt;span&gt;appendChild&lt;/span&gt;&lt;span&gt;(page, videoBlock);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;extracting-audio-for-transcription&quot;&gt;Extracting Audio for Transcription&lt;/h3&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;// Configure audio-only export&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; mimeType&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &apos;audio/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;// Export just the audio track&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; audioBlob&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;export&lt;/span&gt;&lt;span&gt;(page, mimeType, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  targetWidth: &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;  targetHeight: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// audioBlob can now be sent to transcription API&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Setting both dimensions to 0 tells CE.SDK to skip video encoding entirely, making this export much faster than exporting the full video.&lt;/p&gt;
&lt;h3 id=&quot;getting-video-metadata&quot;&gt;Getting Video Metadata&lt;/h3&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;// Get video duration&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; duration&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;getDuration&lt;/span&gt;&lt;span&gt;(videoBlock);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Get dimensions from the fill&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; videoFill&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;getFill&lt;/span&gt;&lt;span&gt;(videoBlock);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; sourceWidth&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;getSourceWidth&lt;/span&gt;&lt;span&gt;(videoFill);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; sourceHeight&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;getSourceHeight&lt;/span&gt;&lt;span&gt;(videoFill);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&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;`Video: ${&lt;/span&gt;&lt;span&gt;sourceWidth&lt;/span&gt;&lt;span&gt;}x${&lt;/span&gt;&lt;span&gt;sourceHeight&lt;/span&gt;&lt;span&gt;}, ${&lt;/span&gt;&lt;span&gt;duration&lt;/span&gt;&lt;span&gt;}s`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;ai-powered-transcription--highlight-detection&quot;&gt;AI-Powered Transcription &amp;#x26; Highlight Detection&lt;/h2&gt;
&lt;h3 id=&quot;the-pipeline&quot;&gt;The Pipeline&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Audio → Transcription:&lt;/strong&gt; Send extracted audio to ElevenLabs Scribe&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transcription → Analysis:&lt;/strong&gt; Feed word-level transcript to Gemini&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Analysis → Timestamps:&lt;/strong&gt; Map AI suggestions back to precise video times&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;transcription-with-speaker-diarization&quot;&gt;Transcription with Speaker Diarization&lt;/h3&gt;
&lt;p&gt;ElevenLabs Scribe v2 provides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Word-level timestamps (start/end time for each word)&lt;/li&gt;
&lt;li&gt;Speaker diarization (which speaker said what)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The output is a structured transcript where each word has a precise timestamp, enabling frame-accurate editing.&lt;/p&gt;
&lt;h3 id=&quot;ai-highlight-detection-with-gemini&quot;&gt;AI Highlight Detection with Gemini&lt;/h3&gt;
&lt;p&gt;The prompt structure matters. Here’s what works:&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;You are analyzing a video transcript to identify segments for short-form content.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;TRANSCRIPT:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[Word-by-word transcript with timestamps]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;TASK:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Identify 3-4 segments that work as standalone short videos. For each segment:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. Find the exact starting and ending words&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. Ensure clean sentence boundaries (no mid-sentence cuts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. Aim for 30-60 second segments&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;OUTPUT FORMAT (JSON):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;concepts&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;      &quot;id&quot;: &quot;concept_1&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;title&quot;: &quot;Hook title&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;description&quot;: &quot;Why this segment works as a standalone clip&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;trimmed_text&quot;: &quot;The exact transcript text to keep...&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;estimated_duration_seconds&quot;: 45&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;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;CRITERIA FOR SELECTION:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Strong hooks (surprising statements, questions, bold claims)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Complete thoughts (don&apos;t cut mid-explanation)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Emotional peaks (humor, insight, controversy)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Standalone value (makes sense without 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&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Before finalizing each segment, ask: &quot;If someone started watching here,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;would they understand what&apos;s being discussed?&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;mapping-back-to-timestamps&quot;&gt;Mapping Back to Timestamps&lt;/h3&gt;
&lt;p&gt;Once Gemini returns the &lt;code&gt;trimmed_text&lt;/code&gt;, we match it against our word-level transcript to find exact timestamps:&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;AI returns:     &quot;The secret to success is actually quite simple...&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Transcript has: [{ word: &quot;The&quot;, start: 45.2 }, { word: &quot;secret&quot;, start: 45.4 }, ...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Result:         Trim video from 45.2s to 52.8s&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This text-matching approach is more reliable than asking the AI to output timestamps directly. LLMs can hallucinate timestamps or miscalculate offsets, but they’re excellent at identifying the right words—so we let the transcript data provide the ground truth for timing.&lt;/p&gt;
&lt;h2 id=&quot;working-with-the-cesdk-timeline&quot;&gt;Working with the CE.SDK Timeline&lt;/h2&gt;
&lt;h3 id=&quot;understanding-blocks&quot;&gt;Understanding Blocks&lt;/h3&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;// Video/Image content&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; graphic&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;graphic&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;// Audio track&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; audio&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;audio&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;// Text overlay&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; text&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;text&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;// Each block can be positioned on the timeline&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.&lt;/span&gt;&lt;span&gt;setTimeOffset&lt;/span&gt;&lt;span&gt;(block, startTimeInSeconds);&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;(block, durationInSeconds);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;manipulating-trim-points&quot;&gt;Manipulating Trim Points&lt;/h3&gt;
&lt;p&gt;Trimming controls which portion of the source media is shown:&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; videoFill&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;getFill&lt;/span&gt;&lt;span&gt;(videoBlock);&lt;/span&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 where in the source video to start (in seconds)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.&lt;/span&gt;&lt;span&gt;setTrimOffset&lt;/span&gt;&lt;span&gt;(videoFill, &lt;/span&gt;&lt;span&gt;45.2&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Set how long to play from that point&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.&lt;/span&gt;&lt;span&gt;setTrimLength&lt;/span&gt;&lt;span&gt;(videoFill, &lt;/span&gt;&lt;span&gt;30.0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Also update the block&apos;s duration to match&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;(videoBlock, &lt;/span&gt;&lt;span&gt;30.0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;working-with-fills-and-their-timing&quot;&gt;Working with Fills and Their Timing&lt;/h3&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;// Get the fill (contains the actual media)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; fill&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;getFill&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;// Fills have their own timing properties&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; trimStart&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;getTrimOffset&lt;/span&gt;&lt;span&gt;(fill);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; trimDuration&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;getTrimLength&lt;/span&gt;&lt;span&gt;(fill);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// The block&apos;s duration should typically match the fill&apos;s trim length&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;(block, trimDuration);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Think of the fill as the media source (which part of the original video to use) and the block as the timeline placement (when and how long it appears). Both need to be updated together for clean edits.&lt;/p&gt;
&lt;h3 id=&quot;creating-time-based-edits-from-transcript-words&quot;&gt;Creating Time-Based Edits from Transcript Words&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; TranscriptWord&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&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;
&lt;span class=&quot;line&quot;&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  end&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;  speaker_id&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;function&lt;/span&gt;&lt;span&gt; applyTranscriptTrim&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; CreativeEngine&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  videoBlock&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;  words&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; TranscriptWord&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;  if&lt;/span&gt;&lt;span&gt; (words.&lt;/span&gt;&lt;span&gt;length&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;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;  const&lt;/span&gt;&lt;span&gt; startTime&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; words[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;].start;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; endTime&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; words[words.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;].end;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; duration&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; endTime &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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; fill&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;getFill&lt;/span&gt;&lt;span&gt;(videoBlock);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  engine.block.&lt;/span&gt;&lt;span&gt;setTrimOffset&lt;/span&gt;&lt;span&gt;(fill, startTime);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  engine.block.&lt;/span&gt;&lt;span&gt;setTrimLength&lt;/span&gt;&lt;span&gt;(fill, 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;setDuration&lt;/span&gt;&lt;span&gt;(videoBlock, duration);&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;generating-speaker-thumbnails&quot;&gt;Generating Speaker Thumbnails&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; function&lt;/span&gt;&lt;span&gt; generateSpeakerThumbnail&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; CreativeEngine&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  videoBlock&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;  timestampSeconds&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;  size&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; 128&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; Promise&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; fill&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;getFill&lt;/span&gt;&lt;span&gt;(videoBlock);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // Seek to the specific timestamp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  engine.block.&lt;/span&gt;&lt;span&gt;setTrimOffset&lt;/span&gt;&lt;span&gt;(fill, timestampSeconds);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  engine.block.&lt;/span&gt;&lt;span&gt;setTrimLength&lt;/span&gt;&lt;span&gt;(fill, &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;// Just a single frame&lt;/span&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 as image&lt;/span&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;export&lt;/span&gt;&lt;span&gt;(videoBlock, &lt;/span&gt;&lt;span&gt;&apos;image/jpeg&apos;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    targetWidth: size,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    targetHeight: size,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;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; URL&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;createObjectURL&lt;/span&gt;&lt;span&gt;(blob);&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 sample multiple timestamps throughout each speaker’s talk time to show different facial angles and expressions—this helps users identify the right person even if they’re looking away or mid-gesture in one frame.&lt;/p&gt;
&lt;h2 id=&quot;speaker-detection--face-tracking&quot;&gt;Speaker Detection &amp;#x26; Face Tracking&lt;/h2&gt;
&lt;h3 id=&quot;why-semi-automatic&quot;&gt;Why Semi-Automatic?&lt;/h3&gt;
&lt;p&gt;Fully automatic speaker detection fails often enough that we added a confirmation step. Users verify detected faces against speaker names from the transcript—takes a few seconds and prevents bad crops on the entire video.&lt;/p&gt;
&lt;h3 id=&quot;how-it-works&quot;&gt;How It Works&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Sample frames throughout the video&lt;/li&gt;
&lt;li&gt;Detect &amp;#x26; cluster faces using face-api.js (runs in browser, no server needed)&lt;/li&gt;
&lt;li&gt;User confirms speaker identities via thumbnails&lt;/li&gt;
&lt;li&gt;Correlate with transcript diarization to map speakers → face locations&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This gives us verified speaker-to-face mapping for dynamic cropping and picture-in-picture layouts.&lt;/p&gt;
&lt;h2 id=&quot;multi-speaker-templates--dynamic-switching&quot;&gt;Multi-Speaker Templates &amp;#x26; Dynamic Switching&lt;/h2&gt;
&lt;h3 id=&quot;the-concept&quot;&gt;The Concept&lt;/h3&gt;
&lt;p&gt;When a video has multiple speakers, we can create layouts that show:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The active speaker prominently&lt;/li&gt;
&lt;li&gt;Other speakers in smaller picture-in-picture views&lt;/li&gt;
&lt;li&gt;Dynamic switching as the conversation flows&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;creating-picture-in-picture-with-cesdk&quot;&gt;Creating Picture-in-Picture with CE.SDK&lt;/h3&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;// Duplicate the video block for each speaker slot&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; pipBlock&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;duplicate&lt;/span&gt;&lt;span&gt;(originalVideoBlock);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Position and size the PiP&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;(pipBlock, &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;engine.block.&lt;/span&gt;&lt;span&gt;setHeight&lt;/span&gt;&lt;span&gt;(pipBlock, &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;engine.block.&lt;/span&gt;&lt;span&gt;setPositionX&lt;/span&gt;&lt;span&gt;(pipBlock, &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;// 20px from left&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.&lt;/span&gt;&lt;span&gt;setPositionY&lt;/span&gt;&lt;span&gt;(pipBlock, &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;// 20px from top&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Enable cropping&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.&lt;/span&gt;&lt;span&gt;setClipped&lt;/span&gt;&lt;span&gt;(pipBlock, &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;setContentFillMode&lt;/span&gt;&lt;span&gt;(pipBlock, &lt;/span&gt;&lt;span&gt;&apos;Cover&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;key-technique-muting-duplicate-audio&quot;&gt;Key Technique: Muting Duplicate Audio&lt;/h3&gt;
&lt;p&gt;When duplicating video blocks for multi-speaker layouts, each copy has its own audio track. We must mute all but one. The &lt;code&gt;setMuted&lt;/code&gt; API operates on the video fill, not the block itself:&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;// For each speaker slot after the first, mute the video fill&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (slotIndex &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;  const&lt;/span&gt;&lt;span&gt; videoFill&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;getFill&lt;/span&gt;&lt;span&gt;(duplicatedBlock);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (videoFill) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    engine.block.&lt;/span&gt;&lt;span&gt;setMuted&lt;/span&gt;&lt;span&gt;(videoFill, &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;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;dynamic-speaker-switching&quot;&gt;Dynamic Speaker Switching&lt;/h3&gt;
&lt;p&gt;As the active speaker changes throughout the video, we:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Detect which speaker is talking (from transcript diarization)&lt;/li&gt;
&lt;li&gt;Swap speaker positions in the template&lt;/li&gt;
&lt;li&gt;Keep the active speaker in the prominent position&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The layout updates automatically as the conversation switches between speakers. We apply different trim offsets to each duplicated block based on the transcript timing—so the main speaker slot shows the person currently talking while PiP slots show the listeners.&lt;/p&gt;
&lt;h2 id=&quot;preview-playback--export&quot;&gt;Preview, Playback &amp;#x26; Export&lt;/h2&gt;
&lt;h3 id=&quot;setting-up-the-canvas&quot;&gt;Setting Up the Canvas&lt;/h3&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; container&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;cesdk-canvas&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.element.&lt;/span&gt;&lt;span&gt;attachTo&lt;/span&gt;&lt;span&gt;(container);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;playback-controls&quot;&gt;Playback Controls&lt;/h3&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;engine.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;engine.player.&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;engine.player.&lt;/span&gt;&lt;span&gt;setPlaybackTime&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;30.5&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;// seek to 30.5 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;const&lt;/span&gt;&lt;span&gt; currentTime&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.player.&lt;/span&gt;&lt;span&gt;getPlaybackTime&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; isPlaying&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.player.&lt;/span&gt;&lt;span&gt;isPlaying&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;syncing-ui-state&quot;&gt;Syncing UI State&lt;/h3&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;engine.player.&lt;/span&gt;&lt;span&gt;onPlaybackTimeChanged&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; time&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.player.&lt;/span&gt;&lt;span&gt;getPlaybackTime&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  updateTimeDisplay&lt;/span&gt;&lt;span&gt;(time);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  updateProgressBar&lt;/span&gt;&lt;span&gt;(time &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; totalDuration);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;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;engine.player.&lt;/span&gt;&lt;span&gt;onPlaybackStateChanged&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;  updatePlayButton&lt;/span&gt;&lt;span&gt;(engine.player.&lt;/span&gt;&lt;span&gt;isPlaying&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;export-options&quot;&gt;Export Options&lt;/h3&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; exportOptions&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;  targetWidth: &lt;/span&gt;&lt;span&gt;1080&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  targetHeight: &lt;/span&gt;&lt;span&gt;1920&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  framerate: &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;  videoBitrate: &lt;/span&gt;&lt;span&gt;8_000_000&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// 8 Mbps&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;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;export&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;  exportOptions,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  (&lt;/span&gt;&lt;span&gt;progress&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; updateProgressBar&lt;/span&gt;&lt;span&gt;(progress &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&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Trigger download&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; url&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;(blob);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; a&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;a.href &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; url;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;a.download &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;shortened-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;a.&lt;/span&gt;&lt;span&gt;click&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For longer videos, consider showing estimated time remaining or allowing background export. Browser export is single-threaded and blocks the tab—a 5-minute export of a 60-second clip isn’t unusual on average hardware, so user feedback is critical.&lt;/p&gt;
&lt;h2 id=&quot;the-finished-app&quot;&gt;The Finished App&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The user flow:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Upload&lt;/strong&gt; → Drop a long-form video into the browser&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Configure&lt;/strong&gt; → Pick output mode (highlights/summary/cleanup) and aspect ratio (9:16, 16:9, 1:1)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verify speakers&lt;/strong&gt; → Match detected faces to transcript speaker names&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Review clips&lt;/strong&gt; → Browse the 3-4 AI-suggested segments, adjust if needed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Choose template&lt;/strong&gt; → Solo speaker, sidecar, stacked, etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Preview&lt;/strong&gt; → Scrub through the timeline, see exactly what you’ll get&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Export&lt;/strong&gt; → Download the final video directly from the browser&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What’s Next&lt;/h2&gt;
&lt;h3 id=&quot;ideas-for-extension&quot;&gt;Ideas for Extension&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Caption style controls:&lt;/strong&gt; Custom fonts, animations, and positioning for subtitles&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;B-roll insertion:&lt;/strong&gt; Automatically add relevant stock footage&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Music &amp;#x26; sound effects:&lt;/strong&gt; AI-selected background audio&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Brand templates:&lt;/strong&gt; Custom overlays, intros, outros&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Batch processing:&lt;/strong&gt; Process multiple videos in sequence&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;taking-it-server-side&quot;&gt;Taking It Server-Side&lt;/h3&gt;
&lt;p&gt;Client-side processing for large files strains browser memory, and users must keep the tab open during export. A hybrid approach works better for production—upload in the background while users edit, then render on a server. You can also offload just the export step—let users build their edits in the browser, then send the CE.SDK scene JSON to your backend for faster, background rendering.&lt;/p&gt;
&lt;p&gt;CE.SDK runs server-side with the same API. For batch processing, background jobs, or offloading rendering from user devices, see the &lt;a href=&quot;https://img.ly/docs/cesdk/renderer/cesdk-renderer-overview-7f3e9a/&quot;&gt;CE.SDK Renderer&lt;/a&gt; for creative automation.&lt;/p&gt;
&lt;h2 id=&quot;resources&quot;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/docs/cesdk/&quot;&gt;CE.SDK Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/docs/cesdk/node/create-video-c41a08/&quot;&gt;CE.SDK Video Editing Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/imgly/videoclipper&quot;&gt;GitHub: Video Shortener Source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://elevenlabs.io/docs/api-reference&quot;&gt;ElevenLabs API Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ai.google.dev/gemini-api/docs&quot;&gt;Gemini API Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Made by &lt;a href=&quot;https://img.ly/&quot;&gt;IMG.LY&lt;/a&gt; with &lt;a href=&quot;https://img.ly/products/creative-sdk&quot;&gt;CE.SDK&lt;/a&gt;&lt;/p&gt;</content:encoded><dc:creator>Eray</dc:creator><media:content url="https://blog.img.ly/2026/01/Gemini_Generated_Image_qj8gi7qj8gi7qj8g.png" medium="image"/><category>AI</category><category>Insights</category><category>Video Editing</category></item><item><title>Automating Video Creation at Scale: Why Templates, Timelines, and Rendering Matter</title><link>https://img.ly/blog/automating-video-creation-at-scale-templates-timelines-rendering/</link><guid isPermaLink="true">https://img.ly/blog/automating-video-creation-at-scale-templates-timelines-rendering/</guid><description>Video automation is fundamentally different from image automation. In this guide we go dive into timelines, rendering performance, and template design; and determine whether video automation scales or breaks in production.</description><pubDate>Mon, 19 Jan 2026 10:46:07 GMT</pubDate><content:encoded>&lt;p&gt;You’ve automated image generation—your marketing team can produce thousands of product banners, social posts, and email headers without touching Photoshop. But when someone asks, “Can we do this for video?” the conversation gets complicated fast.&lt;/p&gt;
&lt;p&gt;Video automation isn’t just “image automation with a timeline.” It’s fundamentally different. A static banner renders in 100 milliseconds. A 30-second video takes 10-30 seconds to render. Your 10MB image template becomes a 50MB video file. And that’s before you factor in audio tracks, transitions, platform-specific encoding requirements, and the reality that “vertical for TikTok” means different specs than “vertical for Instagram Stories.”&lt;/p&gt;
&lt;p&gt;Most teams discover these challenges the hard way—after they’ve already committed to video automation and hit unexpected walls. The template that looked perfect in the editor breaks when the headline is 30% longer in German. The rendering pipeline that handled images fine collapses under video workloads. The licensing you thought you had doesn’t cover H.264 distribution.&lt;/p&gt;
&lt;p&gt;Video automation is solvable. But it requires understanding what makes video different, designing templates that can handle variability, and building infrastructure that respects the complexity.&lt;/p&gt;
&lt;p&gt;In this article we will cover:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why video automation is different&lt;/li&gt;
&lt;li&gt;Designing templates that scale&lt;/li&gt;
&lt;li&gt;Architecture patterns for generation&lt;/li&gt;
&lt;li&gt;Platform constraints &amp;#x26; encoding&lt;/li&gt;
&lt;li&gt;Production pitfalls &amp;#x26; monitoring&lt;/li&gt;
&lt;li&gt;When automation does (and doesn’t) make sense&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;why-video-automation-is-fundamentally-different&quot;&gt;Why Video Automation Is Fundamentally Different&lt;/h2&gt;
&lt;p&gt;If you’ve built image automation pipelines, video introduces layers of complexity that change how you think about templates, rendering, and infrastructure. The core differences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Timeline complexity:&lt;/strong&gt; Video adds time as a dimension—every element has duration, entry/exit timing, and potential animation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-asset coordination:&lt;/strong&gt; Dozens of assets (video clips, audio, overlays) must stay synchronized across time&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rendering performance:&lt;/strong&gt; 100x slower than images (30-second video = 900 frames at 30fps)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;File size:&lt;/strong&gt; 10MB PNG becomes 50MB MP4, impacting storage and distribution&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platform requirements:&lt;/strong&gt; Stricter, more varied specs (aspect ratios, duration limits, codec preferences)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;timeline-complexity-vs-static-composition&quot;&gt;Timeline Complexity vs. Static Composition&lt;/h3&gt;
&lt;p&gt;Images are spatial—elements positioned on a 2D canvas with fixed relationships. Video adds time. Every element now has duration, timing, and animation across the timeline.&lt;/p&gt;
&lt;p&gt;When you generate 10,000 image variants with different text lengths, the layout adjusts spatially. With video, text length affects duration—do you extend the scene to accommodate longer copy, or speed up the animation? Neither is automatic.&lt;/p&gt;
&lt;p&gt;A timeline-based engine like &lt;a href=&quot;https://img.ly/products/video-sdk&quot;&gt;CE.SDK’s video editor&lt;/a&gt; handles this through track systems similar to TikTok or Instagram: &lt;strong&gt;foreground tracks&lt;/strong&gt; for video clips and audio, &lt;strong&gt;background tracks&lt;/strong&gt; that define overall video length. Video clips can be trimmed, repositioned, and arranged. Audio strips sync with video timing but remain independent—you can adjust one without breaking the other.&lt;/p&gt;
&lt;p&gt;The automation challenge: when you inject variable data (product names, customer messages, CTAs), how does that affect the timeline? Template design needs to account for temporal variability, not just spatial.&lt;/p&gt;
&lt;h3 id=&quot;multi-asset-coordination-and-audio-sync&quot;&gt;Multi-Asset Coordination and Audio Sync&lt;/h3&gt;
&lt;p&gt;A single image template might reference 5-10 assets. A video template coordinates dozens: multiple video clips, background music, sound effects, overlay images, animated text, transitions. Each has its own timing, and they must stay synchronized when you generate variations.&lt;/p&gt;
&lt;p&gt;Audio is particularly tricky:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Background music needs to match video duration (fade cleanly, not cut mid-phrase)&lt;/li&gt;
&lt;li&gt;Voiceovers sync with specific visual moments&lt;/li&gt;
&lt;li&gt;Sound effects trigger at precise timestamps&lt;/li&gt;
&lt;li&gt;When automation extends duration (German translation longer), does audio stretch or stay fixed?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Timeline-based engines support audio playback with independent positioning controls. Audio strips appear in the timeline (not on canvas), and you can trim, reposition, and adjust timing independently of video tracks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; Video templates aren’t just asset collections—they’re choreographed sequences where timing relationships matter as much as visual composition.&lt;/p&gt;
&lt;h3 id=&quot;rendering-performance-and-infrastructure-implications&quot;&gt;Rendering Performance and Infrastructure Implications&lt;/h3&gt;
&lt;p&gt;Static image rendering is near-instantaneous. Video rendering is fundamentally slower—every frame is a separate render. A 30-second video at 30fps is 900 frames.&lt;/p&gt;
&lt;p&gt;Infrastructure implications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GPU acceleration isn’t optional&lt;/strong&gt; (CPU-only rendering is prohibitively slow)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Batch processing requires queuing systems&lt;/strong&gt; (can’t render 1,000 videos synchronously)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Progress monitoring becomes essential&lt;/strong&gt; (users need status, not guessing)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Export failures are costlier&lt;/strong&gt; (failed image = 100ms wasted; failed video = 30 seconds)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most video export APIs provide progress callbacks for real-time status:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; videoBlob&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;(page, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  mimeType: &lt;/span&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;  onProgress&lt;/span&gt;&lt;span&gt;: (&lt;/span&gt;&lt;span&gt;rendered&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;encoded&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;total&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;(&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;encoded&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; total&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;designing-templates-that-scale&quot;&gt;Designing Templates That Scale&lt;/h2&gt;
&lt;p&gt;Once you understand why video is different, the next challenge is designing templates that handle variability without manual intervention. Template design determines whether automation breaks or scales.&lt;/p&gt;
&lt;h3 id=&quot;scene-based-modular-structure&quot;&gt;Scene-Based Modular Structure&lt;/h3&gt;
&lt;p&gt;Instead of a single 30-second video, think in scenes: 5-second intro, 15-second product showcase, 5-second CTA, 5-second outro. Each scene is self-contained.&lt;/p&gt;
&lt;p&gt;Benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Variable duration:&lt;/strong&gt; Extend the product scene by 3 seconds for longer German translations without affecting intro/outro&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scene reordering:&lt;/strong&gt; A/B test different sequences (CTA-first vs. product-first)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conditional scenes:&lt;/strong&gt; Include/exclude based on data (show “Limited Time Offer” scene only for promotions)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reusable components:&lt;/strong&gt; Same intro across multiple video types, swap middle scenes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The template creator defines scene boundaries, timing, and which elements automation can modify. Automation code loads the template and injects data without worrying about timeline complexity.&lt;/p&gt;
&lt;h3 id=&quot;text-duration-and-audio-sync-strategies&quot;&gt;Text Duration and Audio Sync Strategies&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Text management approaches:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Extend scene duration:&lt;/strong&gt; Add seconds based on character count (&lt;code&gt;Math.ceil(text.length / 40)&lt;/code&gt; seconds)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reduce text:&lt;/strong&gt; Pass pre-truncated strings that fit fixed timeline&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-line display:&lt;/strong&gt; Let text wrap across frames (works for some designs, breaks others)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Audio sync strategies:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fixed-duration music:&lt;/strong&gt; Choose tracks matching video length exactly (less flexible)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Loopable music:&lt;/strong&gt; Tracks designed to loop seamlessly—calculate loops needed, fade out at end (most reliable)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adaptive music:&lt;/strong&gt; Music that adapts to duration changes (complex but professional)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For automation, loopable music with fade-out is safest. Calculate final video duration after data injection, loop the audio track to cover that duration, apply 1-2 second fade at end.&lt;/p&gt;
&lt;h3 id=&quot;video-clip-placeholders-and-safe-zones&quot;&gt;Video Clip Placeholders and Safe Zones&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Video placeholder logic:&lt;/strong&gt;&lt;br&gt;
Your template might designate a 5-second product demo slot, but actual product videos are 3, 7, or 12 seconds. Options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Trim to fit:&lt;/strong&gt; Longer clips trimmed to match; shorter clips looped (keeps consistent duration, may cut content)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extend the scene:&lt;/strong&gt; Adjust placeholder to match clip length (preserves content, variable duration)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Speed adjustment:&lt;/strong&gt; Speed up/slow down clip to fit (works 0.75x-1.5x, extreme adjustments look awkward)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Safe zones for multi-format export:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Text safe zone:&lt;/strong&gt; Keep all text 10% from edges (device overscan, UI overlays)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Critical content zone:&lt;/strong&gt; Logos, faces, key products within central 70% (survives aspect ratio cropping)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Action-safe zone:&lt;/strong&gt; Don’t place CTAs in corners where platform UI might cover them&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When designing templates that export to multiple aspect ratios (16:9, 9:16, 1:1), test how cropping affects composition.&lt;/p&gt;
&lt;h2 id=&quot;architecture-patterns-for-video-generation-pipelines&quot;&gt;Architecture Patterns for Video Generation Pipelines&lt;/h2&gt;
&lt;p&gt;Once your templates can handle variability, the next challenge is scale: how do you actually generate thousands of videos reliably? Three proven patterns:&lt;/p&gt;
&lt;h3 id=&quot;pattern-1-template-based-batch-rendering&quot;&gt;Pattern 1: Template-Based Batch Rendering&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; Marketing team uploads CSV with 500 products. Overnight, system generates 500 personalized video ads.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;High-level workflow:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Frontend upload interface (CSV, database query, API)&lt;/li&gt;
&lt;li&gt;Job queue (Redis, RabbitMQ, AWS SQS) holds generation requests&lt;/li&gt;
&lt;li&gt;Worker nodes pull jobs, generate videos, upload to storage&lt;/li&gt;
&lt;li&gt;Notification system alerts when batch completes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Batch jobs don’t need real-time feedback. Optimize for throughput over latency—process 100 videos in parallel across 10 worker nodes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Implementation pattern (simplified):&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Worker node processing one batch item&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; engine&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  license: process.env.&lt;/span&gt;&lt;span&gt;CESDK_LICENSE&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; scene&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;loadFromURL&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;templates/product-ad-video.scene&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;// Inject product data via Variables API&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.variable.&lt;/span&gt;&lt;span&gt;setString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;productName&apos;&lt;/span&gt;&lt;span&gt;, product.name);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.variable.&lt;/span&gt;&lt;span&gt;setString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;productPrice&apos;&lt;/span&gt;&lt;span&gt;, product.price);&lt;/span&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; videoBlob&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;(scene, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  mimeType: &lt;/span&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;  h264Profile: &lt;/span&gt;&lt;span&gt;77&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  framerate: &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;  onProgress&lt;/span&gt;&lt;span&gt;: (&lt;/span&gt;&lt;span&gt;rendered&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;encoded&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;total&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;    updateJobProgress&lt;/span&gt;&lt;span&gt;(jobId, encoded &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; total);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;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;await&lt;/span&gt;&lt;span&gt; uploadToS3&lt;/span&gt;&lt;span&gt;(videoBlob, &lt;/span&gt;&lt;span&gt;`output/${&lt;/span&gt;&lt;span&gt;product&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;}.mp4`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scaling:&lt;/strong&gt; Add more worker nodes horizontally. With GPU-enabled instances, one node can process 10-20 videos per minute depending on complexity.&lt;/p&gt;
&lt;p&gt;For large batches (10,000+ videos), use server-side rendering via Docker for maximum throughput. The &lt;a href=&quot;https://img.ly/docs/cesdk/renderer/cesdk-renderer-overview-7f3e9a/&quot;&gt;CE.SDK Renderer&lt;/a&gt; handles GPU acceleration automatically and exports to your specified output directory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Licensing note:&lt;/strong&gt; For production deployments, use licensed Renderer variants (not open-source) to ensure proper H.264/H.265 codec coverage.&lt;/p&gt;
&lt;h3 id=&quot;pattern-2-real-time-personalization&quot;&gt;Pattern 2: Real-Time Personalization&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; User signs up for service. Within 30 seconds, they receive personalized welcome video with their name and company logo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;High-level workflow:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;User action (signup, purchase, milestone) fires event&lt;/li&gt;
&lt;li&gt;Serverless function receives event data&lt;/li&gt;
&lt;li&gt;Engine generates video in real-time (typically 10-30 seconds)&lt;/li&gt;
&lt;li&gt;Video URL returned or sent via email/notification&lt;/li&gt;
&lt;li&gt;Cache generated videos if multiple users might trigger identical content&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Real-time generation provides immediate personalization. Latency (10-30 seconds for a 30-second video) is acceptable when delivered asynchronously.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Video duration is short (under 60 seconds)&lt;/li&gt;
&lt;li&gt;User data is simple (text substitution, not complex asset swapping)&lt;/li&gt;
&lt;li&gt;Delivery is asynchronous (email, notification—not immediate playback)&lt;/li&gt;
&lt;li&gt;Volume is moderate (hundreds per hour, not thousands per minute)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For higher volumes, shift to Pattern 1 (batching) or Pattern 3 (hybrid).&lt;/p&gt;
&lt;h3 id=&quot;pattern-3-hybrid-ui--automation&quot;&gt;Pattern 3: Hybrid UI + Automation&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; Marketing manager designs campaign video template in visual editor. System then automatically generates 50 variants for different audience segments, products, or A/B testing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;High-level workflow:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Designer uses visual editor to create master template with variables&lt;/li&gt;
&lt;li&gt;Saved template becomes reusable asset&lt;/li&gt;
&lt;li&gt;Marketing team specifies which data to inject (audience segments, products)&lt;/li&gt;
&lt;li&gt;Backend loads template, injects data, generates variants&lt;/li&gt;
&lt;li&gt;Generated videos go through approval workflow before deployment&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Separates creative work (design) from scale (automation). Designers don’t write code. Automation doesn’t require developer involvement for template updates.&lt;/p&gt;
&lt;p&gt;Timeline-based engines like &lt;a href=&quot;https://img.ly/blog/ce-sdk-explained-embedded-editor-and-automation-engine-in-one-sdk/&quot;&gt;CE.SDK are explicitly designed for this pattern&lt;/a&gt;—unified workflow between editor and automation. The same template works in both contexts. What the designer sees in the visual editor is exactly what automation generates. No conversion, no drift, perfect consistency.&lt;/p&gt;
&lt;p&gt;This pattern is covered in detail in our &lt;a href=&quot;https://img.ly/blog/editor-or-api-why-modern-creative-automation-needs-both/&quot;&gt;creative automation infrastructure guide&lt;/a&gt;, which explains why teams need both editor and API capabilities within unified workflows.&lt;/p&gt;
&lt;h2 id=&quot;platform-constraints-and-encoding-optimization&quot;&gt;Platform Constraints and Encoding Optimization&lt;/h2&gt;
&lt;p&gt;Generating videos is only half the problem—distribution platforms impose constraints that directly affect how you export.&lt;/p&gt;
&lt;h3 id=&quot;h264-profiles-levels-and-trade-offs&quot;&gt;H.264 Profiles, Levels, and Trade-offs&lt;/h3&gt;
&lt;p&gt;Video export engines support H.264 encoding with configurable parameters affecting quality, compatibility, and file size:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;H.264 Profile:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Baseline (66):&lt;/strong&gt; Broadest device support, lower compression efficiency&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Main (77):&lt;/strong&gt; Good balance (default), works on most modern devices&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;High (100):&lt;/strong&gt; Best quality and compression, requires recent devices&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;H.264 Level (multiply by 10):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Level 3.1 (31) = Up to 720p at 30fps&lt;/li&gt;
&lt;li&gt;Level 4.1 (41) = Up to 1080p at 30fps&lt;/li&gt;
&lt;li&gt;Level 5.2 (52) = Up to 4K at 60fps&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Recommended settings by distribution channel:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Social media ads (Instagram, TikTok, Facebook):&lt;/strong&gt; Main profile (77), Level 4.1 (41), 5-8 Mbps bitrate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;YouTube videos:&lt;/strong&gt; High profile (100), Level 5.2 (52), 8-12 Mbps bitrate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maximum compatibility (email, older devices):&lt;/strong&gt; Baseline profile (66), Level 3.1 (31), 2-4 Mbps bitrate&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Critical constraint:&lt;/strong&gt; H.264 doesn’t support transparency. Transparent areas render with black backgrounds. Handle this at the template level (solid backgrounds or pre-composited elements), not during export.&lt;/p&gt;
&lt;h3 id=&quot;multi-format-export-for-social-platforms&quot;&gt;Multi-Format Export for Social Platforms&lt;/h3&gt;
&lt;p&gt;Social platforms prefer specific aspect ratios:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;9:16 (Vertical):&lt;/strong&gt; TikTok, Instagram Reels, YouTube Shorts, Stories&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;1:1 (Square):&lt;/strong&gt; Instagram Feed, Facebook Feed, LinkedIn Feed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;16:9 (Landscape):&lt;/strong&gt; YouTube, Facebook Watch, LinkedIn Video&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;4:5 (Portrait):&lt;/strong&gt; Instagram Feed alternative, Pinterest&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Your automation pipeline needs to export multiple formats from a single template:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&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; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  { name: &lt;/span&gt;&lt;span&gt;&apos;tiktok&apos;&lt;/span&gt;&lt;span&gt;, width: &lt;/span&gt;&lt;span&gt;1080&lt;/span&gt;&lt;span&gt;, height: &lt;/span&gt;&lt;span&gt;1920&lt;/span&gt;&lt;span&gt; }, &lt;/span&gt;&lt;span&gt;// 9:16&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  { name: &lt;/span&gt;&lt;span&gt;&apos;instagram&apos;&lt;/span&gt;&lt;span&gt;, width: &lt;/span&gt;&lt;span&gt;1080&lt;/span&gt;&lt;span&gt;, height: &lt;/span&gt;&lt;span&gt;1080&lt;/span&gt;&lt;span&gt; }, &lt;/span&gt;&lt;span&gt;// 1:1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  { name: &lt;/span&gt;&lt;span&gt;&apos;youtube&apos;&lt;/span&gt;&lt;span&gt;, width: &lt;/span&gt;&lt;span&gt;1920&lt;/span&gt;&lt;span&gt;, height: &lt;/span&gt;&lt;span&gt;1080&lt;/span&gt;&lt;span&gt; }, &lt;/span&gt;&lt;span&gt;// 16:9&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;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;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; format&lt;/span&gt;&lt;span&gt; of&lt;/span&gt;&lt;span&gt; formats) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; videoBlob&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;(page, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    mimeType: &lt;/span&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;    targetWidth: format.width,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    targetHeight: format.height,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    framerate: &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;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  await&lt;/span&gt;&lt;span&gt; saveToStorage&lt;/span&gt;&lt;span&gt;(videoBlob, &lt;/span&gt;&lt;span&gt;`${&lt;/span&gt;&lt;span&gt;product&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;}-${&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;}.mp4`&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;&lt;strong&gt;Template design consideration:&lt;/strong&gt; Design for the narrowest format (9:16 vertical) as your base. Ensure critical content stays within the central “safe zone” that remains visible when cropped to 1:1 or extended to 16:9.&lt;/p&gt;
&lt;h3 id=&quot;browser-vs-server-side-rendering-trade-offs&quot;&gt;Browser vs. Server-Side Rendering Trade-offs&lt;/h3&gt;
&lt;p&gt;Timeline-based video engines support two rendering environments:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Browser rendering (client-side):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pros: No server infrastructure, renders on user’s device, immediate preview&lt;/li&gt;
&lt;li&gt;Cons: Limited by device performance, requires modern web codecs, &lt;strong&gt;mobile web not supported&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Best for: Interactive editing, single video exports, preview generation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Server-side rendering (Docker):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pros: GPU acceleration, consistent performance, handles high volumes, licensed codecs&lt;/li&gt;
&lt;li&gt;Cons: Requires infrastructure setup, Docker deployment, GPU-enabled instances&lt;/li&gt;
&lt;li&gt;Best for: Batch processing, automation pipelines, production deployments&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;When to use which:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Client-side:&lt;/strong&gt; User-initiated exports, preview generation, low volumes (&amp;#x3C; 10 videos/hour)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server-side:&lt;/strong&gt; Automated generation, batch jobs, high volumes (100+ videos/hour), production reliability&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For hybrid workflows, use client-side rendering for template creation and preview, then switch to server-side for automation and scale.&lt;/p&gt;
&lt;h2 id=&quot;production-pitfalls-and-monitoring&quot;&gt;Production Pitfalls and Monitoring&lt;/h2&gt;
&lt;p&gt;You’ve built a prototype that generates a few test videos. Moving to production requires thinking through edge cases, failure modes, and operational concerns that don’t surface during development.&lt;/p&gt;
&lt;h3 id=&quot;error-handling-and-recovery&quot;&gt;Error Handling and Recovery&lt;/h3&gt;
&lt;p&gt;Video rendering can fail: corrupted assets, invalid template structure, insufficient memory, encoding errors, network timeouts. Your automation needs to handle these gracefully.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Common failure modes:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Asset loading failure:&lt;/strong&gt; Verify asset URLs before generation, use fallback assets&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memory exhaustion:&lt;/strong&gt; Reduce batch size, add memory monitoring, restart workers periodically&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Template validation:&lt;/strong&gt; Check template structure before generation, catch invalid variable names early&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encoding errors:&lt;/strong&gt; Log exact error messages, validate H.264 profile compatibility, check for transparency issues&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Failure recovery pattern (simplified):&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; function&lt;/span&gt;&lt;span&gt; generateVideoWithRetry&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;job&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;maxRetries&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; attempt &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;; attempt &lt;/span&gt;&lt;span&gt;&amp;#x3C;=&lt;/span&gt;&lt;span&gt; maxRetries; attempt&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; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; videoBlob&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;(scene, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        mimeType: &lt;/span&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;        framerate: &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;      });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      return&lt;/span&gt;&lt;span&gt; { success: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;, blob: videoBlob };&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;      if&lt;/span&gt;&lt;span&gt; (attempt &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; maxRetries) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        await&lt;/span&gt;&lt;span&gt; logFailedJob&lt;/span&gt;&lt;span&gt;(job, error);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; { success: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;, error: error.message };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&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; sleep&lt;/span&gt;&lt;span&gt;(Math.&lt;/span&gt;&lt;span&gt;pow&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;, attempt) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 1000&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;// Exponential backoff&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;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;monitoring-and-observability&quot;&gt;Monitoring and Observability&lt;/h3&gt;
&lt;p&gt;Production video automation needs visibility:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key metrics to track:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Generation rate (videos per minute/hour)&lt;/li&gt;
&lt;li&gt;Success rate (percentage completing successfully)&lt;/li&gt;
&lt;li&gt;Average render time (detect performance degradation)&lt;/li&gt;
&lt;li&gt;Queue depth (identify backlogs early)&lt;/li&gt;
&lt;li&gt;Storage usage (monitor file growth and costs)&lt;/li&gt;
&lt;li&gt;Error types (group by category: asset loading, encoding, timeout)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Alert thresholds:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Success rate drops below 95%&lt;/li&gt;
&lt;li&gt;Average render time increases by 50%&lt;/li&gt;
&lt;li&gt;Queue depth exceeds 1,000 pending jobs&lt;/li&gt;
&lt;li&gt;Error rate for specific template exceeds threshold&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;scaling-and-cost-optimization&quot;&gt;Scaling and Cost Optimization&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Horizontal scaling (more worker nodes):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Queue depth consistently high&lt;/li&gt;
&lt;li&gt;Generation rate needs to double or more&lt;/li&gt;
&lt;li&gt;Batch job completion times exceed acceptable limits&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Vertical scaling (bigger instances):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Individual video rendering is slow&lt;/li&gt;
&lt;li&gt;Memory constraints causing failures&lt;/li&gt;
&lt;li&gt;GPU utilization low (CPU bottleneck)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cost optimization:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use spot instances for batch processing (significant savings, acceptable interruption risk)&lt;/li&gt;
&lt;li&gt;Pre-warm worker nodes during off-peak hours for predictable demand spikes&lt;/li&gt;
&lt;li&gt;Archive old generated videos to cold storage after 90 days (S3 Glacier)&lt;/li&gt;
&lt;li&gt;Cache frequently used templates and assets in memory&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;why-most-teams-dont-build-video-automation-in-house&quot;&gt;Why Most Teams Don’t Build Video Automation In-House&lt;/h2&gt;
&lt;p&gt;Before committing to custom development, understand what you’re actually building:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Timeline engines are hard to maintain:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Frame-accurate editing across web, mobile, desktop platforms&lt;/li&gt;
&lt;li&gt;Synchronization between video tracks, audio, overlays, effects&lt;/li&gt;
&lt;li&gt;Undo/redo systems that work across temporal changes&lt;/li&gt;
&lt;li&gt;Template systems with variable timing and duration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Codec licensing is non-trivial:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;H.264/H.265 require patent licenses for distribution&lt;/li&gt;
&lt;li&gt;Open-source codecs lack broad device compatibility&lt;/li&gt;
&lt;li&gt;Licensing costs and compliance complexity&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;GPU rendering infrastructure requires expertise:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Docker orchestration with GPU passthrough&lt;/li&gt;
&lt;li&gt;NVIDIA Container Toolkit configuration&lt;/li&gt;
&lt;li&gt;Performance optimization for parallel rendering&lt;/li&gt;
&lt;li&gt;Cross-platform consistency (web, server, mobile)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Editor + automation parity is rare:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What you design in the editor must match what automation generates&lt;/li&gt;
&lt;li&gt;Same rendering engine, same output quality, no conversion drift&lt;/li&gt;
&lt;li&gt;Template sharing between interactive and headless contexts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Timeline-based engines like &lt;a href=&quot;https://img.ly/products/creative-sdk&quot;&gt;CE.SDK&lt;/a&gt; already solved these problems and expose them via both UI and API. Most teams building video automation use existing engines rather than building from scratch.&lt;/p&gt;
&lt;h2 id=&quot;real-world-use-cases&quot;&gt;Real-World Use Cases&lt;/h2&gt;
&lt;h3 id=&quot;personalized-video-campaigns-at-scale&quot;&gt;Personalized Video Campaigns at Scale&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Financial services company sends 10,000 customers personalized year-end investment summary videos.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Template design:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;40-second video: 5s intro → 20s portfolio visualization → 10s personalized message → 5s CTA&lt;/li&gt;
&lt;li&gt;Variables: &lt;code&gt;customerName&lt;/code&gt;, &lt;code&gt;portfolioReturn&lt;/code&gt;, &lt;code&gt;topHolding1/2/3&lt;/code&gt;, &lt;code&gt;advisorName&lt;/code&gt;, &lt;code&gt;advisorMessage&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Results:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;10,000 videos generated overnight across 50 worker nodes&lt;/li&gt;
&lt;li&gt;Average generation time: 15 seconds per video&lt;/li&gt;
&lt;li&gt;Total processing time: ~3 hours with parallelization&lt;/li&gt;
&lt;li&gt;Delivery: Videos embedded in personalized emails&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This mirrors the use case in our &lt;a href=&quot;https://img.ly/blog/ce-sdk-renderer-creative-automation/&quot;&gt;CE.SDK Renderer article&lt;/a&gt;, where a customer generated “up to 100,000 unique versions” of personalized videos from demographic targeting data.&lt;/p&gt;
&lt;h3 id=&quot;social-media-content-automation&quot;&gt;Social Media Content Automation&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; E-commerce brand launches 200 new products. Marketing needs video ads for each product across TikTok (9:16), Instagram Feed (1:1), and YouTube Shorts (9:16) with different CTA variations for A/B testing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Math:&lt;/strong&gt; 200 products × 3 formats × 2 CTA variants = 1,200 videos&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Distribution:&lt;/strong&gt; Videos automatically upload to Facebook Marketing API and Google Ads API with campaign tags, ready for activation.&lt;/p&gt;
&lt;h2 id=&quot;when-video-automation-makes-sense-and-when-it-doesnt&quot;&gt;When Video Automation Makes Sense (and When It Doesn’t)&lt;/h2&gt;
&lt;p&gt;Video automation isn’t right for every situation.&lt;/p&gt;
&lt;h3 id=&quot;good-fit-high-volume-template-driven-use-cases&quot;&gt;Good Fit: High-Volume, Template-Driven Use Cases&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Video automation works well when:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;High volume:&lt;/strong&gt; Dozens, hundreds, or thousands of videos regularly&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Repetitive structure:&lt;/strong&gt; Videos follow consistent patterns with variable content&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data-driven:&lt;/strong&gt; Content from databases, APIs, or structured data sources&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Time-sensitive:&lt;/strong&gt; Manual production can’t meet speed requirements&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost-prohibitive:&lt;/strong&gt; Hiring video editors or agencies for this volume isn’t viable&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Examples:&lt;/strong&gt; Product videos for e-commerce catalogs, personalized marketing campaigns, social media ads with A/B testing, localized versions (same video in 10+ languages)&lt;/p&gt;
&lt;h3 id=&quot;poor-fit-creative-intensive-one-off-productions&quot;&gt;Poor Fit: Creative-Intensive, One-Off Productions&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Video automation struggles when:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Low volume:&lt;/strong&gt; 1-5 videos per month (manual production is faster)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;High creative variation:&lt;/strong&gt; Each video needs unique storytelling or cinematography&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Complex motion graphics:&lt;/strong&gt; Advanced animation that templates can’t capture&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Live-action footage:&lt;/strong&gt; Real people, locations, scenarios that vary significantly&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Artistic direction:&lt;/strong&gt; Videos requiring subjective creative decisions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Examples:&lt;/strong&gt; Brand commercials with custom concepts, documentary-style content, event recap videos, testimonial videos&lt;/p&gt;
&lt;h3 id=&quot;the-middle-ground-hybrid-approaches&quot;&gt;The Middle Ground: Hybrid Approaches&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Automated foundation + manual refinement:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Generate base videos automatically&lt;/li&gt;
&lt;li&gt;Creative team polishes specific versions for key campaigns&lt;/li&gt;
&lt;li&gt;Use automation for scale, manual work for flagship content&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Manual template creation + automated generation:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Designer creates polished templates once&lt;/li&gt;
&lt;li&gt;Automation generates thousands of variants&lt;/li&gt;
&lt;li&gt;No manual work after template is finalized&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-to-do-next&quot;&gt;What to Do Next&lt;/h2&gt;
&lt;p&gt;Before committing to video automation infrastructure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Evaluate if your use case is template-driven&lt;/strong&gt; (repetitive structure with variable data)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Identify which parts must be automated vs. manual&lt;/strong&gt; (scale vs. creative direction)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test one template end-to-end with real data&lt;/strong&gt; (uncover edge cases early)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Measure render time, cost, and failure rates&lt;/strong&gt; (validate assumptions with real numbers)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;video-automation-solvable-but-not-simple&quot;&gt;Video Automation: Solvable, But Not Simple&lt;/h2&gt;
&lt;p&gt;Video automation is harder than image automation. The timeline adds complexity. Rendering takes 100x longer. File sizes are larger. Platform requirements are stricter. Audio coordination matters. There’s no magic solution that makes these challenges disappear.&lt;/p&gt;
&lt;p&gt;But video automation is solvable when you respect the complexity and build accordingly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Design templates that handle variability&lt;/strong&gt; (text length changes, scene duration flexibility, multi-asset coordination)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Choose the right architecture&lt;/strong&gt; (batch processing for volume, real-time for immediacy, hybrid for collaboration)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimize infrastructure&lt;/strong&gt; (GPU acceleration, server-side rendering, proper codec licensing)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plan for production&lt;/strong&gt; (error handling, monitoring, template versioning, cost management)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The teams that succeed with video automation don’t try to make it simple. They acknowledge it’s complex, design systems that handle that complexity, and automate what can be automated while keeping humans involved where judgment matters.&lt;/p&gt;
&lt;p&gt;Automation doesn’t eliminate work; it shifts work from repetitive execution to thoughtful system design.&lt;/p&gt;
&lt;p&gt;For more context on building creative automation systems that combine human creativity with programmatic scale, see our article on &lt;a href=&quot;https://img.ly/blog/editor-or-api-why-modern-creative-automation-needs-both/&quot;&gt;creative automation infrastructure&lt;/a&gt;. And if you’re ready to explore how timeline-based engines handle video automation specifically, check out the &lt;a href=&quot;https://img.ly/docs/cesdk/js/prebuilt-solutions/automated-video-generation-31187c/&quot;&gt;automated video generation documentation&lt;/a&gt; and &lt;a href=&quot;https://img.ly/docs/cesdk/js/create-video/timeline-editor-912252/&quot;&gt;video timeline editor guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Video automation at scale is possible. It just requires understanding what makes video different and building systems that respect those differences.&lt;/p&gt;
&lt;p&gt;Ready to test it? Start a &lt;a href=&quot;https://img.ly/forms/free-trial&quot;&gt;free trial&lt;/a&gt; or &lt;a href=&quot;https://img.ly/forms/contact-sales&quot;&gt;speak with our sales team&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Klaudia</dc:creator><media:content url="https://blog.img.ly/2026/01/automating-video-content-creation-1.jpg" medium="image"/><category>Insights</category><category>Creative Automation</category><category>Video Editing</category></item><item><title>Best Open Source Video Editor SDKs: 2026 Roundup</title><link>https://img.ly/blog/best-open-source-video-editor-sdks-2025-roundup/</link><guid isPermaLink="true">https://img.ly/blog/best-open-source-video-editor-sdks-2025-roundup/</guid><description>Looking for the best open source video editor SDKs? Read this blog to get a comparison of the top solutions: FFmpeg, MoviePy, OpenShot Library, GStreamer, Remotion, and a commercial alternative: IMG.LY CE.SDK. </description><pubDate>Mon, 10 Nov 2025 11:13:59 GMT</pubDate><content:encoded>&lt;p&gt;If you’ve ever tried building a video editing feature from scratch, you know how quickly things can get complicated. Codecs, pipelines, formats, integrations, and performance tuning all come into play. &lt;/p&gt;
&lt;p&gt;That’s why developers often turn to open-source SDKs that make video processing more accessible and flexible.&lt;/p&gt;
&lt;p&gt;In this guide, we’ll break down some of the most widely used open-source video SDKs, focusing on how easy these tools are to build with, their use cases, and who they’re best for. We’ll also look at where open-source flexibility meets its limits, and how commercial SDKs like IMG.LY can save you time and complexity when building production-ready video products.&lt;/p&gt;
&lt;h2 id=&quot;1-ffmpeg&quot;&gt;&lt;strong&gt;1. FFmpeg&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Let’s start the comparison with one of the most popular open source video editor SDKs. FFmpeg is a command-line multimedia framework that powers much of the modern video ecosystem. It supports everything from transcoding to advanced automation, making it one of the most widely adopted open-source solutions for video processing.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Features &amp;#x26; capabilities:&lt;/strong&gt; FFmpeg includes transcoding, filtering, cropping, resizing, overlaying, color grading, audio processing, and format conversion. Its flexibility comes from its powerful command-line interface, allowing developers to script nearly any video transformation task.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platforms:&lt;/strong&gt; The tool offers broad cross-platform compatibility, running natively on Windows, macOS, and Linux. Its portability allows it to integrate into a wide range of development environments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Building difficulty &amp;#x26; potential roadblocks:&lt;/strong&gt; FFmpeg is quite difficult to learn and implement for beginners. It requires strong knowledge of scripting, codecs, and CLI-based workflows. Due to its steep learning curve, debugging complex filter chains can be time-consuming, and integrating it into GUI-based environments is also challenging. Developers must also navigate potential H.264 commercial licensing restrictions while using this tool.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use Cases:&lt;/strong&gt; FFmpeg is best suited for server-side batch processing, video pipelines, and automated transcoding. While it excels in backend automation, it lacks an interactive editor UI or cross-platform embedding.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Future-proofing, support &amp;#x26; pricing:&lt;/strong&gt; FFmpeg is a mature and stable project used across many industries for years, although its innovation is slow. Most developers using the tool rely on community forums and documentation for troubleshooting and setup help. It’s completely free and open-source, but encoding with certain codecs like H.264 may still require a separate commercial license.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Who it’s for:&lt;/strong&gt; This video editor SDK is ideal for developers handling large-scale backend video processing or transcoding tasks. It’s powerful and reliable, but not beginner-friendly or suited for products needing a visual editing interface.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;comparison-to-imgly&quot;&gt;&lt;strong&gt;Comparison to IMG.LY&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;FFmpeg is purely backend and lacks an interactive editor or cross-platform embedding capabilities. However, it performs exceptionally well in large-scale automation pipelines, where speed, reliability, and format support are critical. IMG.LY CE.SDK, by contrast, gives you a fully embeddable video editor, UI components, and automation workflows in a unified SDK. All without the complexity of manual scripting. &lt;/p&gt;
&lt;p&gt;Check out why &lt;a href=&quot;https://img.ly/ffmpeg-js-alternative&quot;&gt;IMG.LY is a great alternative to FFmpeg&lt;/a&gt; here. &lt;/p&gt;
&lt;h2 id=&quot;2-moviepy&quot;&gt;&lt;strong&gt;2. MoviePy&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;The next open source video editor on the list is MoviePy. It’s a Python library for programmatic video editing. It allows developers to edit video through code, making it particularly useful for automated workflows or content prototyping. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Features &amp;#x26; capabilities:&lt;/strong&gt; It supports cutting, compositing, adding text overlays, transitions, and visual effects. Developers can use Python scripts to build video sequences and automate rendering.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platforms:&lt;/strong&gt; MoviePy works across all major platforms that support Python. It runs in standard Python environments without requiring extra setup, which makes it accessible for most developers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Building difficulty &amp;#x26; potential roadblocks:&lt;/strong&gt; MoviePy is easier to use than FFmpeg, but still requires scripting. It’s suitable for Python developers for prototyping, but it can struggle with performance when handling large video files. Developers often face challenges with dependency management and the absence of a real-time UI.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use cases:&lt;/strong&gt; This tool is best for automated content generation, research, and rapid prototyping of video workflows. It’s ideal for proof-of-concept projects where scripting flexibility matters more than performance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Future-proofing, support &amp;#x26; pricing:&lt;/strong&gt; MoviePy is actively maintained by its niche community of developers. While it serves a smaller user base compared to larger frameworks, its reliability and accessibility make it a consistent choice for Python-based video projects. You can find support through community documentation that explains most functions clearly. It’s free and open-source, which makes it especially appealing for experimentation, research, and smaller-scale automation setups.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Who its for:&lt;/strong&gt; MoviePy is a great choice if you’re a Python developer wanting automation or experimental workflows. However, it’s not ideal for production-scale or interactive use cases due to performance limits and the absence of a UI. &lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;comparison-to-imgly-1&quot;&gt;&lt;strong&gt;Comparison to IMG.LY&lt;/strong&gt; &lt;/h3&gt;
&lt;p&gt;MoviePy is designed for scripting and backend automation. It works best for creating short automated clips or experimental workflows where developers want full control through Python code. &lt;/p&gt;
&lt;p&gt;IMG.LY CE.SDK goes beyond backend scripting by offering ready-to-embed editors, automation APIs, and full multi-platform support for web and mobile. This makes it a more complete choice if your team needs both automation and interactive editing within products.  &lt;/p&gt;
&lt;h2 id=&quot;3-openshot-library--mlt-framework&quot;&gt;&lt;strong&gt;3. OpenShot Library / MLT Framework&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;The OpenShot Library, built on the MLT Framework, is a C++/Python library for video compositing and timeline editing. It powers the OpenShot video editor and can be integrated into custom desktop applications.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Features &amp;#x26; capabilities:&lt;/strong&gt; It provides timeline editing, transitions, effects, and audio mixing. Developers can implement advanced video composition features similar to traditional desktop editors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platforms:&lt;/strong&gt; The library is cross-platform, working reliably across major desktop operating systems, including Windows, macOS, and Linux. It can be integrated into various environments, which makes it useful if you’re building desktop editors or applications that require timeline-based video editing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Building difficulty &amp;#x26; potential roadblocks:&lt;/strong&gt; The integration complexity is medium-high, as the API can be unstable, and managing dependencies can be difficult. Developers may also encounter documentation gaps, making it challenging to set up and maintain. Integrating the library into a GUI is not straightforward, and limited server-side automation means it is less suited for scalable or cloud-based workflows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use cases:&lt;/strong&gt; OpenShot Library is suitable for you if you’re looking to build desktop-based editors, experimental timeline tools, or custom video composition applications.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Future-proofing, support &amp;#x26; pricing:&lt;/strong&gt; The framework is actively maintained and works well for desktop video editing applications. However, it is not designed for server-side automation or large-scale deployment. The support is community-driven, so the updates and fixes depend on volunteer contributions, which can affect long-term reliability for enterprise use. The framework is free and open-source, making it accessible to anyone looking to experiment or build lightweight video editing tools.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Who its for:&lt;/strong&gt; OpenShot/MLT fits developers building desktop-focused editors who need timeline control but can manage API and dependency challenges. It’s less practical for cloud or mobile workflows.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;comparison-to-imgly-2&quot;&gt;&lt;strong&gt;Comparison to IMG.LY&lt;/strong&gt; &lt;/h3&gt;
&lt;p&gt;While OpenShot/MLT excels in desktop-based timeline editing, it is better suited for applications that prioritize local processing and desktop workflows. &lt;/p&gt;
&lt;p&gt;IMG.LY CE.SDK, on the other hand, offers embedded, cross-platform editors with built-in automation, making it a more versatile choice for SaaS and enterprise environments that need scalability, faster integration, and an intuitive editing experience for end users.&lt;/p&gt;
&lt;h2 id=&quot;4-gstreamer&quot;&gt;&lt;strong&gt;4. GStreamer&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;GStreamer is a multimedia framework for building video and audio pipelines. It’s a foundational tool in many streaming and broadcast systems and allows developers to create complex pipelines for capturing, processing, and outputting media in real time.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Features &amp;#x26; capabilities:&lt;/strong&gt; This open source video editor supports real-time processing, filters, and complex pipelines for media processing and streaming. Developers can construct customized processing chains for advanced workflows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platforms:&lt;/strong&gt; GStreamer is cross-platform, supporting multiple programming languages with bindings for C, Python, Java, and Rust. You can run it on Windows, macOS, and Linux, and integrate it into both desktop and server environments. This makes it flexible for building diverse media workflows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Building difficulty &amp;#x26; potential roadblocks:&lt;/strong&gt; Building with GStreamer is complex. It requires deep knowledge of pipelines and plugins, and debugging can be time-intensive. UI integration adds another layer of difficulty.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use cases:&lt;/strong&gt; Despite its building difficulty, developers use GStreamer for real-time video streaming, server-side processing, and for broadcast applications requiring precise control over data flow.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Future-proofing, support &amp;#x26; pricing:&lt;/strong&gt; GStreamer is mature and widely used in the media industry. It continues to be a reliable choice for building streaming and broadcast systems, but it requires significant development expertise to configure and maintain. Developers often rely on community resources for support, though some commercial assistance is available. It’s free and open-source, which makes it accessible for experimentation and custom media workflows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Who it’s for:&lt;/strong&gt; The tool is ideal for developers who need real-time video pipelines or streaming capabilities. But it may not be a fit for applications requiring easy embedding or interactive UIs. &lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;comparison-to-imgly-3&quot;&gt;&lt;strong&gt;Comparison to IMG.LY&lt;/strong&gt; &lt;/h3&gt;
&lt;p&gt;GStreamer operates at the infrastructure level, ideal for backend media processing where you need precise control over data flow and performance. It gives you the flexibility to build custom video pipelines, but doing so requires significant technical effort and time. &lt;/p&gt;
&lt;p&gt;IMG.LY provides a ready-to-embed, interactive editor with automation and multi-platform support, so you can focus on building user experiences rather than managing low-level media infrastructure.&lt;/p&gt;
&lt;h2 id=&quot;5-remotion&quot;&gt;&lt;strong&gt;5. Remotion&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Remotion enables developers to create videos programmatically using React components. Developers can define video scenes, transitions, and animations directly in React. While it’s a flexible approach, building long or high-resolution videos can demand careful optimization to ensure smooth performance.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Features &amp;#x26; capabilities:&lt;/strong&gt; It supports timeline-based video composition, text overlays, animations, audio integration, and programmatic transitions, all rendered through React. Developers can create videos entirely with code, defining scenes and effects inside React components.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platforms:&lt;/strong&gt; Remotion is built for Node.js and browser-based environments via headless rendering. You can render videos directly in the browser or on a Node.js server, which gives developers flexibility in how they deploy and automate video creation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Building difficulty &amp;#x26; potential roadblocks:&lt;/strong&gt; Building difficulty using Remotion is at a medium level because it requires React and JavaScript proficiency. The potential issues you might face while using this include debugging challenges with high-resolution or long-duration renders, along with limited server-side optimization that can affect scalability and performance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use cases:&lt;/strong&gt; Remotion is well-suited for automated video generation, marketing content, and dynamic SaaS video templates. You can use it to create personalized videos or promotional content at scale, using data or user inputs to generate customized output.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Future-proofing, support &amp;#x26; pricing:&lt;/strong&gt; The tool is actively maintained and is built with a modern web stack using React and TypeScript, which ensures long-term stability and adaptability. You can find help through detailed documentation, GitHub issues, and an engaged developer community. While it doesn’t offer formal support, its open-source nature encourages collaboration and contributions that keep the framework improving. Like other tools we’ve discussed, this is also free and open-source, making it an accessible choice for developers exploring programmatic video creation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Who it’s for:&lt;/strong&gt; Based on its features and capabilities, it’s best for React developers who want to generate videos programmatically. However, it lacks an interactive editor and may not scale easily for enterprise automation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;comparison-to-imgly-4&quot;&gt;&lt;strong&gt;Comparison to IMG.LY&lt;/strong&gt; &lt;/h3&gt;
&lt;p&gt;Remotion excels in programmatic video creation, allowing you to build videos entirely through code with precision and flexibility. However, it lacks interactive editing tools or native SDKs that can be directly embedded into products. &lt;/p&gt;
&lt;p&gt;IMG.LY CE.SDK, by comparison, gives you both automation capabilities and user-facing editors across platforms, helping teams build complete video experiences without maintaining separate systems. You can find more information on &lt;a href=&quot;https://img.ly/remotion-alternative&quot;&gt;how IMG.LY compares to Remotion here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;6-imgly-cesdk&quot;&gt;&lt;strong&gt;6. IMG.LY CE.SDK&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;A commercial solution that offers enterprise-grade functionality and developer-friendly implementation.&lt;/p&gt;
&lt;p&gt;IMG.LY CreativeEditor SDK is built for interactive video editing and automation. It gives developers the tools to build powerful, embeddable video editors without the complexity of starting from scratch. It balances the flexibility developers expect from open-source frameworks with the stability and support of a commercial-grade platform.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Features &amp;#x26; capabilities:&lt;/strong&gt; It includes timeline editing, multi-layer video composition, templates, AI-assisted editing, transitions, video export, and server-side automation. The SDK simplifies complex workflows by offering prebuilt editors and APIs for integration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platforms:&lt;/strong&gt; Available on Web, iOS, Android, React Native, Flutter, Node.js, and Electron. This broad platform support allows developers to build consistent video editing experiences across desktop, mobile, and web environments. Whether you’re integrating into an existing app or launching a new product, the IMG.LY CE SDK ensures a unified editing interface and workflow across all platforms.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Building difficulty &amp;#x26; potential roadblocks:&lt;/strong&gt; Integration is straightforward, with minimal setup required, making it the easiest SDK to build with among all the tools we compared in this list. Most implementation effort lies in customizing workflows rather than solving technical barriers. This simplicity comes from well-documented APIs, prebuilt UI components, and consistent cross-platform behavior, which together reduce the development time. Potential roadblocks are minimal, allowing you to focus on building product features instead of managing infrastructure or compatibility issues.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use cases:&lt;/strong&gt; IMG.LY is best suited for SaaS apps, marketing automation, social content tools, and video personalization platforms that need seamless interactive editing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Future-proofing, support &amp;#x26; pricing:&lt;/strong&gt; &lt;a href=&quot;https://img.ly/whats-new&quot;&gt;Frequent releases&lt;/a&gt;, an expanding AI roadmap, and a scalable engine ensure long-term reliability for developers and enterprises. You get access to dedicated onboarding engineers, solution architects, and enterprise SLAs that make implementation and scaling easier. Pricing follows a custom enterprise licensing model, offering flexibility for different business needs. But you can &lt;a href=&quot;https://img.ly/forms/free-trial&quot;&gt;take a free trial to get started.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Who it’s for: I&lt;/strong&gt;MG.LY is designed for companies that need scalable, interactive video editing and automation in one package. Its ideal for teams seeking low development overhead and enterprise-grade reliability.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-imgly-stands-out-as-the-best-open-source-video-editor-sdk&quot;&gt;&lt;strong&gt;Why IMG.LY stands out as the best open source video editor SDK&lt;/strong&gt; &lt;/h3&gt;
&lt;p&gt;IMG.LY combines interactive editing, server-side automation, and multi-platform SDKs in one stack, giving you everything needed to build scalable, modern video products. You can create, edit, and automate workflows within the same ecosystem without relying on separate tools. None of the open-source options on this list deliver this level of integration and consistency across web, mobile, and desktop environments.&lt;/p&gt;
&lt;p&gt;Check out how IMG.LY has helped over 600 innovative startups, government entities, and Fortune 500 companies to streamline their design, video, and photo editing workflows. &lt;a href=&quot;https://img.ly/case-studies&quot;&gt;Read our case studies here.&lt;/a&gt; &lt;/p&gt;
&lt;h2 id=&quot;overview-table&quot;&gt;&lt;strong&gt;Overview table&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Here’s a brief overview of all the video editor SDKs we’ve discussed to help you choose the right one. &lt;/p&gt;

















































































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Feature / Use Case&lt;/th&gt;&lt;th&gt;&lt;strong&gt;FFmpeg&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;MoviePy&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;OpenShot / MLT&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;GStreamer&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;Remotion&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;IMG.LY CE.SDK&lt;/strong&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Timeline editing&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;❌ No GUI timeline; timeline-like operations only possible via scripting in CLI.&lt;/td&gt;&lt;td&gt;⚠️ Timeline functionality simulated through Python scripts; suitable for small-scale projects.&lt;/td&gt;&lt;td&gt;✅ Native timeline-based video editing with GUI; library supports composition though GUI integration can be limited.&lt;/td&gt;&lt;td&gt;⚠️ Pipeline-centric; timeline must be assembled programmatically, no native GUI timeline.&lt;/td&gt;&lt;td&gt;✅ Timeline created programmatically using React components; no interactive GUI timeline.&lt;/td&gt;&lt;td&gt;✅ Full-featured interactive timeline, multi-layer support, drag-and-drop UI&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Filters &amp;#x26; Effects&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;✅ Supports CLI-based filters, overlays, color correction; requires manual configuration&lt;/td&gt;&lt;td&gt;✅ Python effects library; scripting needed to combine filters&lt;/td&gt;&lt;td&gt;✅ Built-in video effects and transitions; limited prebuilt UI&lt;/td&gt;&lt;td&gt;✅ Plugin-based filters; flexible but requires pipeline setup&lt;/td&gt;&lt;td&gt;✅ Animations, text overlays, audio effects via scripting; programmatic transitions&lt;/td&gt;&lt;td&gt;✅ AI-enhanced effects, transitions, multi-layer composition, background removal&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Server-side automation&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;✅ Excellent for backend batch processing and transcoding; CLI automation is robust&lt;/td&gt;&lt;td&gt;✅ Python scripting enables automated content generation&lt;/td&gt;&lt;td&gt;⚠️ Limited server-side automation; mainly library-level&lt;/td&gt;&lt;td&gt;✅ Suitable for large-scale pipelines and streaming&lt;/td&gt;&lt;td&gt;✅ Automated video rendering via Node.js; works with server-side scripts&lt;/td&gt;&lt;td&gt;✅ Full automation APIs; combines server-side rendering + interactive editing in one SDK&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Ease of embedding&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;❌ CLI only; difficult to embed in apps&lt;/td&gt;&lt;td&gt;⚠️ Python scripts can be integrated, but no GUI&lt;/td&gt;&lt;td&gt;⚠️ Library-only; integration requires custom UI&lt;/td&gt;&lt;td&gt;⚠️ Complex pipelines; requires deep development knowledge&lt;/td&gt;&lt;td&gt;⚠️ Node.js / headless browser integration possible; UI must be custom-built&lt;/td&gt;&lt;td&gt;✅ Ready-to-embed SDKs for Web, iOS, Android, React Native, Flutter; includes prebuilt editors&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Difficulty building / integration&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;High — complex CLI usage, codec handling, and scripting required&lt;/td&gt;&lt;td&gt;Medium — Python knowledge required, scripting-intensive&lt;/td&gt;&lt;td&gt;Medium-High — API can be unstable, dependencies must be managed&lt;/td&gt;&lt;td&gt;High — pipeline setup and plugin management are challenging&lt;/td&gt;&lt;td&gt;Medium — React knowledge needed; performance optimization may be required&lt;/td&gt;&lt;td&gt;Low — SDK provides prebuilt editor components and APIs for easy integration&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Potential roadblocks&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;CLI complexity, codec licensing (H.264), debugging pipelines, no GUI&lt;/td&gt;&lt;td&gt;Large videos may be slow, performance issues, dependency management&lt;/td&gt;&lt;td&gt;API instability, documentation gaps, integration with UI requires effort&lt;/td&gt;&lt;td&gt;Steep learning curve, pipeline debugging, plugin version conflicts&lt;/td&gt;&lt;td&gt;Rendering performance for long/high-resolution videos, debugging programmatic scripts&lt;/td&gt;&lt;td&gt;Minimal; most challenges relate to workflow decisions during integration&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Cross-platform support&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;✅ Desktop/server; Windows, macOS, Linux&lt;/td&gt;&lt;td&gt;✅ Desktop/server; cross-platform Python&lt;/td&gt;&lt;td&gt;✅ Desktop/server; cross-platform&lt;/td&gt;&lt;td&gt;✅ Desktop/server; cross-platform&lt;/td&gt;&lt;td&gt;✅ Node.js + headless browser; works across server environments&lt;/td&gt;&lt;td&gt;✅ Web + mobile + desktop; consistent UI across platforms&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;AI / Automation&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;❌ None&lt;/td&gt;&lt;td&gt;❌ None&lt;/td&gt;&lt;td&gt;❌ None&lt;/td&gt;&lt;td&gt;❌ None&lt;/td&gt;&lt;td&gt;⚠️ Programmatic automation via scripting&lt;/td&gt;&lt;td&gt;✅ AI-assisted editing, automation workflows, plugin ecosystem&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;✅ Excellent for batch and server pipelines&lt;/td&gt;&lt;td&gt;⚠️ Limited; performance depends on scripting and hardware&lt;/td&gt;&lt;td&gt;⚠️ Medium; suitable for desktop-scale apps&lt;/td&gt;&lt;td&gt;✅ Large-scale pipelines, real-time streaming&lt;/td&gt;&lt;td&gt;✅ Programmatic generation can scale, but may need server resources&lt;/td&gt;&lt;td&gt;✅ Enterprise-grade; cloud-ready, multi-platform, high-volume video workflows&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Free, OSS; H.264 encoding may require commercial license&lt;/td&gt;&lt;td&gt;Free, OSS&lt;/td&gt;&lt;td&gt;Free, OSS&lt;/td&gt;&lt;td&gt;Free, OSS&lt;/td&gt;&lt;td&gt;Free, OSS&lt;/td&gt;&lt;td&gt;Custom enterprise licensing&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Best For&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Developers building automation pipelines, server-side video processing, or streaming workflows&lt;/td&gt;&lt;td&gt;Python developers building scripted video content and automation&lt;/td&gt;&lt;td&gt;Developers building desktop-focused video apps with timeline editing&lt;/td&gt;&lt;td&gt;Developers needing real-time pipelines or advanced streaming&lt;/td&gt;&lt;td&gt;Developers using React for programmatic video generation and templating&lt;/td&gt;&lt;td&gt;Companies needing interactive, embedded video editing + AI automation + multi-platform SDK&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;choosing-the-right-video-sdk-how-to-make-the-right-choice&quot;&gt;&lt;strong&gt;Choosing the right video SDK: How to make the right choice&lt;/strong&gt; &lt;/h2&gt;
&lt;p&gt;When choosing the right video SDK, the best option depends on what you’re building and who it’s for.&lt;/p&gt;
&lt;p&gt;Choose open-source SDKs like FFmpeg, MoviePy, OpenShot/MLT, GStreamer, and Remotion if you prefer flexibility and complete control. These tools are free, customizable, and well-suited for automation, research, or proof-of-concept projects. However, they often require significant development time, scripting knowledge, and ongoing maintenance, which can slow down production or scaling.&lt;/p&gt;
&lt;p&gt;For teams building commercial or enterprise-ready video products, commercial SDKs offer a more practical route. They save you time on integration, provide dedicated support, and come with consistent updates to ensure reliability across platforms.&lt;/p&gt;
&lt;p&gt;That’s why developers choose IMG.LY SDK.&lt;/p&gt;
&lt;p&gt;It combines interactive editing, automation, and cross-platform SDKs in a single stack, something open-source solutions can’t fully replicate. It’s designed for SaaS, social, and enterprise use cases where scalability, ease of use, and performance matter most. You can embed editors, automate workflows, and scale confidently without managing multiple systems.&lt;/p&gt;
&lt;p&gt;So if you want a complete, production-ready platform with AI features, automation, and an intuitive editing experience, IMG.LY is the right choice. &lt;/p&gt;
&lt;p&gt;Get in touch with our team to see how it’s deployed in over 500 applications and used by millions of users across 29 countries. &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://img.ly/forms/free-trial&quot;&gt;Start a free trial&lt;/a&gt; or &lt;a href=&quot;https://img.ly/forms/contact-sales&quot;&gt;book your call today&lt;/a&gt;!&lt;/p&gt;</content:encoded><dc:creator>Vatsala</dc:creator><media:content url="https://blog.img.ly/2025/11/open-source-video-editor-sdk-whitelabel-best-comparison--1-.jpg" medium="image"/><category>Video Editing</category><category>Video Editor</category><category>OpenSource</category><category>Insights</category></item><item><title>Animate Between Images - AI-Native Video Workflows with CE.SDK and Veo 3</title><link>https://img.ly/blog/animate-between-images-ai-native-video-workflows-with-ce-sdk-and-veo-3/</link><guid isPermaLink="true">https://img.ly/blog/animate-between-images-ai-native-video-workflows-with-ce-sdk-and-veo-3/</guid><description>See how we integrated Veo 3.1 into CE.SDK to animate between images in seconds. With generation times as low as 9 s, this demo shows how easily you can embed AI-native workflows from stills to smooth video clips directly inside your Creative Editor.</description><pubDate>Wed, 22 Oct 2025 14:10:31 GMT</pubDate><content:encoded>&lt;p&gt;With the release of &lt;a href=&quot;https://aistudio.google.com/models/veo-3&quot;&gt;&lt;strong&gt;Veo 3.1&lt;/strong&gt;,&lt;/a&gt; we wanted to show just how effortless it is to embed generative AI capabilities directly into creative workflows. In this quick demo, we integrated Veo 3 into &lt;strong&gt;CreativeEditor SDK (CE.SDK)&lt;/strong&gt; enabling users to &lt;strong&gt;animate between two still images&lt;/strong&gt; in just a few clicks.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/ai-launch/IMG-LY-CE-SDK-veo-3-1-video-design-editor-sdk.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h3 id=&quot;from-still-images-to-motion&quot;&gt;From Still Images to Motion&lt;/h3&gt;
&lt;p&gt;In our demo, we start with two images of the same person, one wearing a hat, the other without. Inside the editor, users simply select both images, click the &lt;strong&gt;AI context button&lt;/strong&gt;, and choose &lt;strong&gt;“Animate between images.”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The images are loaded into a side panel where users can optionally add a &lt;strong&gt;text prompt&lt;/strong&gt; to guide the transition. Once generated, the resulting short video is placed directly on the canvas ready for editing, compositing, or export.&lt;/p&gt;
&lt;p&gt;What’s particularly impressive is the &lt;strong&gt;generation speed&lt;/strong&gt;. In this example, Veo 3.1 produced a smooth &lt;strong&gt;8-second transition in just 9 seconds&lt;/strong&gt; a major improvement compared to earlier versions. This speed makes iterative creative workflows feel fluid and responsive, bridging the gap between prompt-driven generation and real-time editing.&lt;/p&gt;
&lt;h3 id=&quot;try-it-out&quot;&gt;Try It Out&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;Try out Veo 3.1&amp;#39;s magic in CE.SDK.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1312px) 1312px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1312&quot; height=&quot;310&quot; src=&quot;https://img.ly/_astro/veo-fal-ai-video-editor-sdk-ce_sdk-imgly_Z2d7vDi.webp&quot; srcset=&quot;/_astro/veo-fal-ai-video-editor-sdk-ce_sdk-imgly_z0ja6.webp 640w, /_astro/veo-fal-ai-video-editor-sdk-ce_sdk-imgly_Z1ivUdP.webp 750w, /_astro/veo-fal-ai-video-editor-sdk-ce_sdk-imgly_Zs5oeI.webp 828w, /_astro/veo-fal-ai-video-editor-sdk-ce_sdk-imgly_ZkhHeU.webp 1080w, /_astro/veo-fal-ai-video-editor-sdk-ce_sdk-imgly_Z20tepT.webp 1280w, /_astro/veo-fal-ai-video-editor-sdk-ce_sdk-imgly_Z2d7vDi.webp 1312w&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can check out the implementation on &lt;a href=&quot;https://github.com/imgly/plugins/blob/main/examples/ai/src/pages/Veo31Example.tsx&quot;&gt;GitHub&lt;/a&gt; and give Veo 3.1 a spin inside your CE.SDK instance (sign up for a &lt;a href=&quot;https://img.ly/forms/free-trial&quot;&gt;free trial&lt;/a&gt; if you haven’t already).&lt;/p&gt;
&lt;h3 id=&quot;a-glimpse-of-ai-native-editing&quot;&gt;A Glimpse of AI-Native Editing&lt;/h3&gt;
&lt;p&gt;This simple feature highlights how easy it is to make your editor &lt;strong&gt;AI-native,&lt;/strong&gt; combining traditional editing tools with generative intelligence.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Practical use cases include:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;E-commerce&lt;/strong&gt;&lt;br&gt;
Show products “in action” or animate between styles and configurations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Marketing&lt;/strong&gt;&lt;br&gt;
Create quick product reveal animations from static assets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content creation&lt;/strong&gt;&lt;br&gt;
Generate short motion clips or “tween” between creative scenes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And if users want longer clips, they can simply &lt;strong&gt;add another 8-second track&lt;/strong&gt; and &lt;strong&gt;transition seamlessly&lt;/strong&gt; into the next.&lt;/p&gt;
&lt;h3 id=&quot;the-future-of-creative-workflows&quot;&gt;The Future of Creative Workflows&lt;/h3&gt;
&lt;p&gt;With Veo 3 integrated, CE.SDK becomes a powerful playground for AI-driven creativity from image-to-video to scene interpolation and contextual animation.&lt;/p&gt;
&lt;p&gt;We already empowered over 600 innovative startups, government entities, and Fortune 500 companies to add powerful design, video, and photo editing workflows to their products. &lt;a href=&quot;https://img.ly/forms/contact-sales?ref=img.ly&quot;&gt;Get in touch&lt;/a&gt;, to see how we can do the same for you.&lt;/p&gt;</content:encoded><dc:creator>Jan</dc:creator><dc:creator>Marcin</dc:creator><media:content url="https://blog.img.ly/2025/10/veo-3_1-creative-editor-sdk-video-editor-imgly-2.jpg" medium="image"/><category>AI</category><category>Video Editing</category><category>Image2Video</category></item><item><title>How to Run FFmpeg on AWS Spot Instances for Scalable, Low-Cost Video Processing</title><link>https://img.ly/blog/how-to-run-ffmpeg-on-aws-spot-instances-for-scalable-low-cost-video-processing/</link><guid isPermaLink="true">https://img.ly/blog/how-to-run-ffmpeg-on-aws-spot-instances-for-scalable-low-cost-video-processing/</guid><description>Learn how to run FFmpeg on AWS Spot Instances for scalable, cost-effective video processing. This step-by-step guide covers S3 setup, installing FFmpeg, handling Spot interruptions, and building resilient cloud workflows.</description><pubDate>Mon, 06 Oct 2025 08:25:43 GMT</pubDate><content:encoded>&lt;p&gt;In this guide, we’ll explore how to harness &lt;a href=&quot;https://docs.aws.amazon.com/&quot;&gt;Amazon Web Services (AWS)&lt;/a&gt; for video processing with FFmpeg. You’ll learn why the cloud is so helpful for handling resource-heavy video tasks, how to set up your AWS Spot Instance and S3 bucket, and how to run FFmpeg jobs at scale. Along the way, we’ll get acquainted with &lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances.html&quot;&gt;AWS Spot Instances&lt;/a&gt;, a cost-saving compute option that can make large-scale video processing far more affordable. If you’re new to the tool itself, start with our &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg/&quot;&gt;Ultimate Guide to FFmpeg.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;By the end of this article, you’ll know how to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set up AWS with S3 storage and a Spot instance.&lt;/li&gt;
&lt;li&gt;Install and test FFmpeg in the cloud.&lt;/li&gt;
&lt;li&gt;Run video processing jobs on a Spot Instance while keeping costs low.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Whether you’re new to cloud computing or just looking for a practical way to scale your FFmpeg workflows, this guide will walk you through the essentials step by step.&lt;/p&gt;
&lt;p&gt;Lets dive into by setting up the AWS environment.&lt;/p&gt;
&lt;h2 id=&quot;set-up-your-aws-environment&quot;&gt;&lt;strong&gt;Set Up Your AWS Environment&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Before you can run FFmpeg in the cloud, you need to prepare your AWS environment. This involves creating an account, setting up storage, and launching an instance where you’ll install and run FFmpeg. Let’s break it down step by step.&lt;/p&gt;
&lt;h3 id=&quot;create-an-aws-account-and-s3-bucket&quot;&gt;Create an AWS Account and S3 Bucket&lt;/h3&gt;
&lt;p&gt;If you don’t already have one, sign up for an &lt;a href=&quot;https://aws.amazon.com/&quot;&gt;AWS account&lt;/a&gt;. Once logged into the AWS Management Console, the first thing to set up is an &lt;a href=&quot;https://docs.aws.amazon.com/AmazonS3/latest/userguide/GetStartedWithS3.html&quot;&gt;&lt;strong&gt;Amazon S3 bucket&lt;/strong&gt;&lt;/a&gt;. S3 (Simple Storage Service) is AWS’s object storage system, and it’s ideal for hosting both your input and output video files. For example, you can upload raw videos to S3, process them with FFmpeg, and then store the converted files back in the same bucket. When creating your bucket, be sure to choose a region close to where you expect to run your compute resources, as this reduces latency and transfer costs.&lt;/p&gt;
&lt;h3 id=&quot;launch-a-spot-instance&quot;&gt;Launch a Spot Instance&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Spot Instances&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances.html&quot;&gt;AWS Spot Instances&lt;/a&gt; are virtual machines that let you access unused cloud capacity at significantly reduced prices, sometimes up to 90% cheaper than regular On-Demand Instances. The catch is that they can be &lt;strong&gt;interrupted&lt;/strong&gt; by AWS with little notice if the capacity is needed elsewhere. For workloads like video processing, which can be restarted or distributed across multiple nodes, Spot Instances are a perfect fit. They allow developers to take advantage of lower costs without sacrificing performance, provided they design workflows with fault tolerance in mind. This balance of affordability and scalability makes Spot Instances a popular choice for running FFmpeg jobs in the cloud.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Setup a Spot Instance&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;From the AWS console, go to the EC2 dashboard and launch a new instance. Choose &lt;strong&gt;Ubuntu Server&lt;/strong&gt; or &lt;strong&gt;Amazon Linux&lt;/strong&gt; as the operating system, since both have excellent community support and are well-documented. When selecting an instance type, start with something lightweight like &lt;code&gt;t2.micro&lt;/code&gt; (eligible for the free tier), but keep in mind you may need more powerful instances (with higher CPU or GPU) for larger video workloads. Open the &lt;strong&gt;Advanced Details&lt;/strong&gt;, under &lt;strong&gt;Purchasing option&lt;/strong&gt;, check the box labeled &lt;strong&gt;Spot Instances&lt;/strong&gt; to tell AWS you want to use discounted spare capacity instead of On-Demand pricing. You can also set a maximum price per instance hour if you want to control costs more tightly, but in most cases leaving it at the default ensures you’ll simply pay the current Spot market price, which automatically adjusts with supply and demand and is never higher than the standard On-Demand rate.&lt;/p&gt;
&lt;p&gt;You can also choose between a &lt;strong&gt;persistent&lt;/strong&gt; or &lt;strong&gt;one-time&lt;/strong&gt; request type in the &lt;strong&gt;Customize Spot Instance options&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Once the instance is created, you can confirm that its lifecycle is &lt;strong&gt;spot&lt;/strong&gt; under the instance details section. This indicates that a Spot Instance has been successfully created.&lt;/p&gt;
&lt;h3 id=&quot;connect-to-your-instance-via-ssh&quot;&gt;Connect to Your Instance via SSH&lt;/h3&gt;
&lt;p&gt;Once your Spot instance is running, you can connect to it remotely using &lt;strong&gt;SSH (Secure Shell)&lt;/strong&gt;. During instance setup, AWS will prompt you to create or download a &lt;strong&gt;key pair&lt;/strong&gt; (&lt;code&gt;.pem&lt;/code&gt; file). Keep this file safe, it’s your private key for secure access. On your local machine, open a terminal at the location where you saved the key pair file and run:&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;ssh&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; &quot;your-key.pem&quot;&lt;/span&gt;&lt;span&gt; &amp;#x3C;&lt;/span&gt;&lt;span&gt;usernam&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;your-instance-public-dn&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace &lt;code&gt;your-key.pem&lt;/code&gt; with the path to your private key file and &lt;code&gt;&amp;#x3C;your-instance-public-dns&gt;&lt;/code&gt; with the public DNS address of your Spot instance (visible in the console). You can get the exact SSH command for your instance by clicking the &lt;strong&gt;Connect&lt;/strong&gt; button in the EC2 dashboard and selecting the &lt;strong&gt;SSH client&lt;/strong&gt; option. Once connected, you’ll be inside the environment where you can install FFmpeg and start running commands. For a smooth operation, make yourself familiar with basic shell commands for the operating system you opted for in the instance. Examples for various shell commands on a linux system can be found &lt;a href=&quot;https://www.geeksforgeeks.org/linux-unix/basic-shell-commands-in-linux/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;install-ffmpeg-on-the-spot-instance&quot;&gt;Install FFmpeg on the Spot Instance&lt;/h2&gt;
&lt;p&gt;With your spot instance up and running, the next step is to install &lt;strong&gt;FFmpeg&lt;/strong&gt; so you can start processing videos. Installation is straightforward, and once complete, you’ll test it with a simple command to confirm everything works.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update Your Package Lists&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Before installing any software, it’s a good practice to make sure your system’s package list is up to date. Run:&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;sudo&lt;/span&gt;&lt;span&gt; apt&lt;/span&gt;&lt;span&gt; update&lt;/span&gt;&lt;span&gt; &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; apt&lt;/span&gt;&lt;span&gt; upgrade&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command refreshes the list of available software and ensures your instance has the latest security patches. &lt;em&gt;(If you’re using Amazon Linux instead of Ubuntu, replace this with &lt;code&gt;sudo yum update -y&lt;/code&gt;.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Install FFmpeg&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;On Ubuntu, FFmpeg is available directly through the official package repository:&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;sudo&lt;/span&gt;&lt;span&gt; apt&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For Amazon Linux, the process may require enabling extra repositories first:&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;sudo&lt;/span&gt;&lt;span&gt; amazon-linux-extras&lt;/span&gt;&lt;span&gt; enable&lt;/span&gt;&lt;span&gt; epel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; yum&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will download and install FFmpeg along with its dependencies. To avoid dependency issues, consider &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;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Verify the Installation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After installation, check that FFmpeg is available 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;ffmpeg&lt;/span&gt;&lt;span&gt; -version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see output showing the installed FFmpeg version and configuration details.&lt;/p&gt;
&lt;h2 id=&quot;run-ffmpeg-on-a-spot-instance&quot;&gt;Run FFmpeg on a Spot Instance&lt;/h2&gt;
&lt;p&gt;Now that you’ve installed FFmpeg on a regular Spot instance, it’s time to make evrything work together. In this section, upload a video to Amazon S3, and then run FFmpeg on the spot instance to process the video and save the output.&lt;/p&gt;
&lt;p&gt;While it’s possible to upload videos directly to the instance and process them there, this approach isn’t reliable since the instance can be terminated at any time when AWS reclaims capacity. The better and safer practice is to use the &lt;strong&gt;S3 bucket&lt;/strong&gt;, which ensures your files are stored safely and don’t need to be re-uploaded each time. This is especially important when working with large video files or multiple inputs, as S3 provides a more reliable and scalable storage solution. Prefer Google Cloud? See our tutorial on &lt;a href=&quot;https://img.ly/blog/ffmpeg-on-google-cloud-platform-guide/&quot;&gt;running FFmpeg on GCP&lt;/a&gt; for similar steps.&lt;/p&gt;
&lt;h3 id=&quot;upload-a-sample-video-to-s3&quot;&gt;&lt;strong&gt;Upload a Sample Video to S3&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Before running FFmpeg, you’ll need a video file available in your S3 bucket. From your local machine terminal (not the instance terminal), use the AWS CLI to upload a sample file:&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;aws&lt;/span&gt;&lt;span&gt; s3&lt;/span&gt;&lt;span&gt; cp&lt;/span&gt;&lt;span&gt; sample.mp4&lt;/span&gt;&lt;span&gt; s3://your-bucket-name/input/sample.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This stores &lt;code&gt;sample.mp4&lt;/code&gt; inside the &lt;code&gt;input&lt;/code&gt; folder of your bucket. Organizing files into &lt;code&gt;input&lt;/code&gt; and &lt;code&gt;output&lt;/code&gt; directories is a good practice for video workflows. You can do the same using the S3 dashboard in AWS. If your workflow involves cropping or trimming videos before upload, check out our Flutter guide on &lt;a href=&quot;https://img.ly/blog/how-to-crop-and-trim-videos-in-flutter/&quot;&gt;crop and trim videos in Flutter.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you are using AWS CLI for the first time in you machine, then setup your credentials using &lt;code&gt;aws configure&lt;/code&gt; command. See &lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/reference/configure/&quot;&gt;here&lt;/a&gt; for more help.&lt;/p&gt;
&lt;h3 id=&quot;run-ffmpeg-on-the-spot-instance&quot;&gt;&lt;strong&gt;Run FFmpeg on the Spot Instance&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Once your Spot Instance is running, connect to it via SSH (the same way you did earlier). Before we get the video from the S3 bucket, you should set an IAM Role for the instance to access the S3 bucket. For that, go to &lt;strong&gt;IAM&lt;/strong&gt;&gt;&lt;strong&gt;Roles&lt;/strong&gt;&gt;&lt;strong&gt;Create Role,&lt;/strong&gt; select EC2 from use case dropdown menu. On the next page, search for &lt;code&gt;AmazonS3FullAccess&lt;/code&gt; and select it, give a role name and finaly create it. Now the instances can access the files from the bucket.&lt;/p&gt;
&lt;p&gt;Since the instance is a new system, you will need to install &lt;code&gt;awscli&lt;/code&gt; from &lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html&quot;&gt;here&lt;/a&gt;. You will also have to setup credentials using &lt;code&gt;aws configure&lt;/code&gt; command as mentioned above. Then, copy the input file from S3 down to your instance:&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;aws&lt;/span&gt;&lt;span&gt; s3&lt;/span&gt;&lt;span&gt; cp&lt;/span&gt;&lt;span&gt; s3://your-bucket-name/input/sample.mp4&lt;/span&gt;&lt;span&gt; sample.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, run FFmpeg to process the video. For example, converting MP4 to WebM:&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;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; sample.mp4&lt;/span&gt;&lt;span&gt; output.webm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After processing, upload the result back to S3:&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;aws&lt;/span&gt;&lt;span&gt; s3&lt;/span&gt;&lt;span&gt; cp&lt;/span&gt;&lt;span&gt; output.webm&lt;/span&gt;&lt;span&gt; s3://your-bucket-name/output/output.webm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now access your processed file directly in S3 and even configure it for delivery via Amazon CloudFront if needed.&lt;/p&gt;
&lt;p&gt;By running FFmpeg jobs on Spot Instances, you can save up to &lt;strong&gt;90% of costs compared to On-Demand pricing&lt;/strong&gt;, making this setup ideal for batch video processing.&lt;/p&gt;
&lt;p&gt;You can refer to &lt;a href=&quot;https://github.com/imgly/blog-ffmpeg-aws-spot-instances&quot;&gt;this&lt;/a&gt; github repository for examples.&lt;/p&gt;
&lt;h2 id=&quot;spot-instances-vs-aws-lambda-for-ffmpeg&quot;&gt;Spot Instances vs AWS Lambda for FFmpeg&lt;/h2&gt;
&lt;p&gt;When running FFmpeg on AWS, you have more than one option for compute. Two of the most common choices are &lt;strong&gt;AWS Lambda&lt;/strong&gt; and &lt;strong&gt;EC2 Spot Instances&lt;/strong&gt;. Each has its strengths and trade-offs, and the best fit depends on the type of video workload you’re running.&lt;/p&gt;
&lt;h3 id=&quot;aws-lambda-for-lightweight-tasks&quot;&gt;AWS Lambda for Lightweight Tasks&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/welcome.html&quot;&gt;AWS Lambda&lt;/a&gt; is Amazon’s &lt;em&gt;serverless compute service&lt;/em&gt;—you don’t need to manage servers, and your code runs only when triggered. It’s easy to set up and integrates seamlessly with other AWS services such as S3 and API Gateway. For example, you can automatically run an FFmpeg job as soon as a new video is uploaded to your S3 bucket.&lt;/p&gt;
&lt;p&gt;However, Lambda comes with limitations: functions can only run for a maximum of &lt;strong&gt;15 minutes&lt;/strong&gt;, and they have limited temporary storage (512MB by default, extendable up to 10GB). This makes Lambda a great choice for lightweight tasks like generating thumbnails, clipping short videos, or converting smaller files, but not for large-scale processing.&lt;/p&gt;
&lt;h3 id=&quot;ec2-spot-instances-for-heavy-workloads&quot;&gt;EC2 Spot Instances for Heavy Workloads&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;EC2 Spot Instances&lt;/strong&gt;, by contrast, are designed for &lt;strong&gt;long and heavy workloads&lt;/strong&gt;. Spot Instances give you access to unused EC2 capacity at discounts of up to &lt;strong&gt;90% compared to On-Demand pricing&lt;/strong&gt;. Since they are full-fledged EC2 machines, Spot Instances don’t impose runtime limits or storage caps.&lt;/p&gt;
&lt;p&gt;This makes them ideal for &lt;strong&gt;large video files, long-running transcodes, and GPU-accelerated workloads&lt;/strong&gt; such as 4K/8K encoding or complex filter pipelines. The trade-off is that Spot Instances can be &lt;strong&gt;interrupted by AWS&lt;/strong&gt; with two minutes of notification when EC2 needs the capacity back, so jobs need to be either restartable or split into smaller tasks.&lt;/p&gt;
&lt;h3 id=&quot;rule-of-thumb&quot;&gt;Rule of Thumb&lt;/h3&gt;
&lt;p&gt;A simple guideline to choose between the two is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Small, quick jobs → use AWS Lambda&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Large, long jobs but restartable → use EC2 Spot Instances&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By matching the right AWS service to your workload, you get the best balance of cost, performance, and reliability for running FFmpeg in the cloud.&lt;/p&gt;
&lt;h2 id=&quot;handling-spot-interruptions&quot;&gt;Handling Spot Interruptions&lt;/h2&gt;
&lt;h3 id=&quot;two-minute-warning&quot;&gt;Two-Minute Warning&lt;/h3&gt;
&lt;p&gt;One important aspect of using &lt;strong&gt;Spot Instances&lt;/strong&gt; is that they can be interrupted at any time if AWS needs the capacity back. When this happens, AWS provides a &lt;strong&gt;two-minute warning&lt;/strong&gt; before the instance is terminated. For video processing workflows, this means you need to plan for interruptions so that work isn’t lost midway.&lt;/p&gt;
&lt;h3 id=&quot;break-videos-into-smaller-chunks&quot;&gt;Break Videos into Smaller Chunks&lt;/h3&gt;
&lt;p&gt;A common strategy is to &lt;strong&gt;break long videos into smaller chunks&lt;/strong&gt; before processing. For example, instead of transcoding a two-hour video in one run, you can split it into 10-minute segments, process each segment separately, and then stitch them back together later. This way, if an interruption occurs, only the current segment is affected rather than the entire job. FFmpeg supports segmenting input files using options like &lt;code&gt;-ss&lt;/code&gt; (start time) and &lt;code&gt;-t&lt;/code&gt; (duration), which makes this approach straightforward.&lt;/p&gt;
&lt;h3 id=&quot;store-partial-results-in-s3&quot;&gt;Store Partial Results in S3&lt;/h3&gt;
&lt;p&gt;Another best practice is to &lt;strong&gt;store partial results in Amazon S3&lt;/strong&gt; as soon as they are completed. For instance, after processing each video chunk, upload the output back to your S3 bucket immediately. This ensures that even if the Spot Instance is terminated, your progress is safe, and you can resume processing from the last completed chunk instead of starting over. Using S3 as a central storage layer makes your workflow fault-tolerant and resilient against interruptions.&lt;/p&gt;
&lt;h3 id=&quot;reliable-and-cost-effective-workflows&quot;&gt;Reliable and Cost-Effective Workflows&lt;/h3&gt;
&lt;p&gt;By designing your FFmpeg workflows with these strategies, you can take full advantage of the &lt;strong&gt;cost savings of Spot Instances&lt;/strong&gt; without risking data loss. This makes Spot instances a reliable option even for long-running or large-scale video processing jobs, provided you build in restart and recovery mechanisms.&lt;/p&gt;
&lt;h2 id=&quot;tips--next-steps&quot;&gt;Tips &amp;#x26; Next Steps&lt;/h2&gt;
&lt;p&gt;Once you’ve set up FFmpeg on AWS and experimented with Spot Instances, there are a few ways to refine your workflow and explore more advanced options. These next steps will help you get the most out of your cloud-based video processing setup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Try Different Instance Types&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;AWS offers many EC2 instance families, each optimized for different workloads. For general transcoding and compression tasks, &lt;strong&gt;compute-optimized instances&lt;/strong&gt; like the &lt;code&gt;c5&lt;/code&gt; family are often a good fit, providing strong CPU performance at a reasonable cost. If you’re working with high-resolution videos or need hardware acceleration, &lt;strong&gt;GPU-based instances&lt;/strong&gt; such as the &lt;code&gt;g4dn&lt;/code&gt; family can dramatically speed up encoding with FFmpeg’s GPU-enabled libraries. Experimenting with instance types lets you balance performance and cost for your specific use case.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Orchestrate Jobs with SQS or Step Functions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As your workflows grow, running jobs manually can become inefficient. AWS provides orchestration tools to help automate FFmpeg jobs. &lt;strong&gt;Amazon SQS (Simple Queue Service)&lt;/strong&gt; can be used to queue video processing tasks, allowing multiple Spot Instances to work on jobs in parallel. For a full Docker‑based pipeline with REST APIs and worker queues, see our &lt;a href=&quot;https://img.ly/blog/building-a-production-ready-batch-video-processing-server-with-ffmpeg/&quot;&gt;production‑ready batch video processing server&lt;/a&gt; guide. For more complex workflows, &lt;strong&gt;AWS Step Functions&lt;/strong&gt; provide a way to chain together multiple tasks such as fetching input files, running FFmpeg, handling errors, and saving outputs into a reliable, serverless state machine. These services ensure your video pipelines are scalable and fault-tolerant without requiring you to build custom orchestration logic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Consider CE.SDK as an Alternative&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you’re building custom editing workflows on AWS, consider integrating a frontend editor like our &lt;a href=&quot;https://img.ly/products/creative-sdk&quot;&gt;CreativeEditor SDK.&lt;/a&gt; While FFmpeg and AWS handle the heavy lifting of transcoding and storage, CE.SDK provides a &lt;strong&gt;powerful in-browser editing interface&lt;/strong&gt; for trimming, adding overlays, applying filters, or creating design templates. This combination allows you to deliver both &lt;strong&gt;scalable backend video processing&lt;/strong&gt; and &lt;strong&gt;interactive, user-facing editing tools&lt;/strong&gt;, similar to what platforms like Canva provide.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Running &lt;strong&gt;FFmpeg on AWS Spot Instances&lt;/strong&gt; gives you a powerful and cost-effective way to handle large-scale video transcoding. By leveraging Spot pricing, you can cut costs by up to 90% compared to On-Demand instances, making it possible to process even long, high-resolution videos without breaking the budget. With strategies like chunking videos and storing partial outputs in S3, interruptions become manageable, allowing you to balance affordability with reliability.&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;smaller, lightweight, or event-driven workflows&lt;/strong&gt;, &lt;strong&gt;AWS Lambda&lt;/strong&gt; is a perfect complement. Its serverless nature makes it easy to trigger FFmpeg jobs automatically, for example, generating thumbnails or clipping short clips as soon as a file is uploaded to S3. Although Lambda has strict runtime and storage limits, it shines when simplicity and automation are priorities.&lt;/p&gt;
&lt;p&gt;As a &lt;strong&gt;next step&lt;/strong&gt;, you can begin experimenting with automation and orchestration using services like &lt;strong&gt;SQS, Step Functions, or even AWS Batch&lt;/strong&gt; to build resilient pipelines that combine the strengths of both Spot Instances and Lambda. For teams looking for a fully managed alternative, CE.SDK is also worth exploring.&lt;/p&gt;
&lt;p&gt;By now, you’ve seen how to install FFmpeg, run it on spot instance, and handle serverless workflows with Lambda. With these tools and strategies, you’re ready to design &lt;strong&gt;flexible, scalable, and affordable video processing pipelines&lt;/strong&gt; in the AWS cloud.&lt;/p&gt;
&lt;p&gt;Join 3,000+ creative professionals who get early access to new features and updates—&lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i?ref=img.ly&quot;&gt;subscribe&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Robin</dc:creator><media:content url="https://blog.img.ly/2025/10/ffmpeg-awe.jpg" medium="image"/><category>FFmpeg</category><category>Video Editing</category><category>Server-side Video</category></item><item><title>How To: Video Generation With Javascript</title><link>https://img.ly/blog/how-to-video-generation-with-javascript/</link><guid isPermaLink="true">https://img.ly/blog/how-to-video-generation-with-javascript/</guid><description>Learn how to programmatically create videos at scale with Javascript and CreativeEditor SDK.</description><pubDate>Wed, 22 Jan 2025 09:53:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Follow this tutorial to learn how to programmatically create videos in JavaScript directly in the browser.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In 2025 video is firmly established as powerhouse for engagement, gaining even more traction with the rise of short-form videos. After all, as humans, we naturally resonate with video content more than any other type of media.&lt;/p&gt;
&lt;p&gt;The problem is that video generation is challenging—and doing it within the browser through JavaScript makes things even more complex. Fortunately, production-grade solutions like &lt;a href=&quot;https://img.ly/docs/cesdk/js/prebuilt-solutions/video-editor-9e533a/&quot;&gt;CreativeEditor SDK (CE.SDK)&lt;/a&gt; make the process not only possible but also easy to implement.&lt;/p&gt;
&lt;p&gt;In this guide, you will learn how to programmatically generate videos in the browser via JavaScript using CreativeEditor SDK. We will cover various applications, including merging videos, adding audio tracks, and even integrating AI features.&lt;/p&gt;
&lt;p&gt;Let’s dive in!&lt;/p&gt;
&lt;h2 id=&quot;why-programmatic-video-generation-matters&quot;&gt;Why Programmatic Video Generation Matters&lt;/h2&gt;
&lt;p&gt;Videos are dominating social media, marketing, and online platforms such as e-commerce platforms (e.g., to showcase products). Numerous studies have proven that &lt;a href=&quot;https://thesocialshepherd.com/blog/video-marketing-statistics&quot;&gt;video is the most effective form of media&lt;/a&gt; for marketing, as it resonates deeply with us.&lt;/p&gt;
&lt;p&gt;As the demand for personalized and dynamic content increases, traditional video production methods are becoming inefficient. The proliferation of channels with different demands on size and video quality as well as the need to create personalized videos at scale means that automated video creation is becoming indispensable. Most existing solutions run batch processing of videos on the server after gathering input data from various source. However, while server-side processing quickly becomes costly and introduces significant communication overhead client devices have become more powerful than ever.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://img.ly/products/creative-sdk&quot;&gt;CreativeEditor SDK&lt;/a&gt; bridges that gap by simplifying programmatic video generation in JavaScript within modern browsers. With CE.SDK, you can automate repetitive tasks and define scalable, personalized, and engaging video creation workflows, ensuring each generated video resonates with its target audience.&lt;/p&gt;
&lt;h2 id=&quot;get-started-with-cesdk-for-javascript&quot;&gt;Get Started with CE.SDK for JavaScript&lt;/h2&gt;
&lt;p&gt;Now that we established why programmatic video generation is important, you are ready to get started with the tool designed for it: CreativeEditor SDK.&lt;/p&gt;
&lt;p&gt;Learn how to integrate CE.SDK for video editing into a vanilla JavaScript application!&lt;/p&gt;
&lt;p&gt;You can access the code for this tutorial on &lt;a href=&quot;https://github.com/imgly/video-generation-js&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;requirements&quot;&gt;Requirements&lt;/h3&gt;
&lt;p&gt;The only prerequisites to follow this tutorial are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A modern browser&lt;/strong&gt;: CreativeEditor SDK runs directly in your browser’s JavaScript engine, so no additional setup is required. Any &lt;a href=&quot;https://caniuse.com/wasm&quot;&gt;browser supporting WASM&lt;/a&gt; is enough.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A CreativeEditor SDK license&lt;/strong&gt;: If you do not have one yet, &lt;a href=&quot;https://img.ly/forms/free-trial&quot;&gt;sign up for a free trial&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your web application uses Node.js, ensure you have the &lt;a href=&quot;https://nodejs.org/en/download&quot;&gt;latest stable versions of both Node.js and NPM installed&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;import-the-library&quot;&gt;Import the library&lt;/h3&gt;
&lt;p&gt;In a vanilla JavaScript application, create a JavaScript module file (e.g., video-editor.js) and import the CreativeEditor SDK engine library inside it:&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; CreativeEngine &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;&amp;#x3C;https://cdn.img.ly/packages/imgly/cesdk-engine/1.42.0/index.js&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: In this example, the SDK is served from our CDN for convenience. In a production environment, it is recommended to host all &lt;a href=&quot;https://img.ly/docs/cesdk/js/serve-assets-b0827c/&quot;&gt;assets and libraries on your own servers&lt;/a&gt; for improved control and performance.&lt;/p&gt;
&lt;p&gt;To use the video-editor.js module in an HTML page, import it with this line:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;module&quot;&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;video-editor.js&quot;&lt;/span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules&quot;&gt;type=“module”&lt;/a&gt; attribute tells the browser to treat the script as an ES6 module, so that you can use import and export statements.&lt;/p&gt;
&lt;p&gt;Alternatively, if you are using a module bundler like Webpack, Rollup, Parcel, or Vite, add the &lt;a href=&quot;https://www.npmjs.com/package/@cesdk/engine&quot;&gt;@cesdk/engine&lt;/a&gt; npm package to your project’s dependencies:&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;In this case, you can then import the headless SDK into your JavaScript code as follows:&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; CreativeEngine &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@cesdk/engine&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Well done!&lt;/p&gt;
&lt;h2 id=&quot;setting-up-your-environment&quot;&gt;&lt;strong&gt;Setting Up Your Environment&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;In your JavaScript module, right below the library import, initialize the &lt;a href=&quot;https://img.ly/docs/cesdk/node/get-started/overview-e18f40/&quot;&gt;CreativeEditor SDK headless engine&lt;/a&gt; using the following 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;// src/video-editor.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; CreativeEngine &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;&amp;#x3C;https://cdn.img.ly/packages/imgly/cesdk-engine/1.42.0/index.js&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;// 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;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;CreativeEngine.&lt;/span&gt;&lt;span&gt;init&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;engine&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;  // attach the engine canvas to the DOM&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&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;cesdk_container&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;append&lt;/span&gt;&lt;span&gt;(engine.element);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // do something with your instance of CreativeEditor SDK...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // detach the engine and clean up its resource&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  engine.element.&lt;/span&gt;&lt;span&gt;remove&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;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;/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 in your CreativeEditor SDK subscription.&lt;/p&gt;
&lt;p&gt;Ensure that the HTML page importing &lt;code&gt;video-editor.js&lt;/code&gt; contains the following &lt;code&gt;&amp;#x3C;div&gt;&lt;/code&gt; element with the &lt;code&gt;cesdk_container&lt;/code&gt; ID:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;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;width: 100%; height: 100vh;&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href=&quot;https://img.ly/docs/cesdk/js/get-started/overview-e18f40/&quot;&gt;&lt;code&gt;CreativeEngine.init()&lt;/code&gt;&lt;/a&gt; method will mount the editor engine component within this div.&lt;/p&gt;
&lt;p&gt;Congratulations! You have successfully integrated the video editing engine.&lt;/p&gt;
&lt;h2 id=&quot;load-a-video&quot;&gt;&lt;strong&gt;Load a Video&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Use the logic below inside the body of the CreativeEndine.init() callback function to load a video in CE.SDK:&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;// initialize a new video scene&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; scene&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.scene.&lt;/span&gt;&lt;span&gt;createVideo&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 a page block and attach it to the scene&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; page&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;page&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;appendChild&lt;/span&gt;&lt;span&gt;(scene, page);&lt;/span&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 dimensions of the video page&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;(page, &lt;/span&gt;&lt;span&gt;720&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;setHeight&lt;/span&gt;&lt;span&gt;(page, &lt;/span&gt;&lt;span&gt;1280&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 a graphic block to hold the video content&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// and set its shape to a rectangle&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; video&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;graphic&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;setShape&lt;/span&gt;&lt;span&gt;(video, engine.block.&lt;/span&gt;&lt;span&gt;createShape&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;rect&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;// create a fill type for the video and set the video file URI&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; videoFill&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;createFill&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;video&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;setString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  videoFill,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;fill/video/fileURI&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;&amp;#x3C;https://cdn.img.ly/assets/demo/v2/ly.img.video/videos/pexels-drone-footage-of-a-surfer-barrelling-a-wave-12715991.mp4&gt;&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;// apply the video fill to the video graphic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.&lt;/span&gt;&lt;span&gt;setFill&lt;/span&gt;&lt;span&gt;(video, videoFill);&lt;/span&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 a track block to manage the video timeline&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// and attache the track to the page and the video graphic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// to the track&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; track&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;track&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;appendChild&lt;/span&gt;&lt;span&gt;(page, 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;appendChild&lt;/span&gt;&lt;span&gt;(track, video);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// make the track cover the parent 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;fillParent&lt;/span&gt;&lt;span&gt;(track);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This initializes a video scene using &lt;a href=&quot;https://img.ly/docs/cesdk/js/concepts/scenes-e8596d/&quot;&gt;&lt;code&gt;createVideo()&lt;/code&gt;&lt;/a&gt; and creates a &lt;code&gt;&quot;graphic&quot;&lt;/code&gt; block to display the video in a rectangle, adding it to the page. Next, it loads a video from a remote resource and appends it as a track to the parent block.&lt;/p&gt;
&lt;p&gt;For a complete explanation of how the above code works, &lt;a href=&quot;https://img.ly/docs/cesdk/js/create-video-c41a08/&quot;&gt;refer to the documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you comment out the cleanup part, this is what you should see in your browser application:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 608px) 608px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;608&quot; height=&quot;697&quot; src=&quot;https://img.ly/_astro/JS-video-generation-video-fill_Z1LSqkq.webp&quot; srcset=&quot;/_astro/JS-video-generation-video-fill_Z1LSqkq.webp 608w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Notice how the video from the remote URI has been loaded into the &lt;/p&gt;&lt;div&gt; element where CreativeEditor SDK is attached to the page’s DOM.Time to explore more programmatic video generation features!&lt;p&gt;&lt;/p&gt;
&lt;h2 id=&quot;programmatic-video-generation-features&quot;&gt;Programmatic Video Generation Features&lt;/h2&gt;
&lt;p&gt;Follow the use cases below to see how CE.SDK makes automated video generation in JavaScript easier.&lt;strong&gt;Generate Video in Different Formats&lt;/strong&gt; CE.SDK supports video export in &lt;a href=&quot;https://img.ly/docs/cesdk/js/file-format-support-3c4b2a/&quot;&gt;MP4 format&lt;/a&gt; &lt;a href=&quot;https://img.ly/docs/cesdk/faq/video-support/?platform=node&quot;&gt;&lt;/a&gt;(with MP3 audio) with different resolutions and aspect ratios.To export a video with a specific aspect ratio, first adjust the width and height of the parent block to which the video block is attached:&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;// specify an aspect ratio of 9:16 (ideal for vertical videos)&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;(page, &lt;/span&gt;&lt;span&gt;720&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;setHeight&lt;/span&gt;&lt;span&gt;(page, &lt;/span&gt;&lt;span&gt;1280&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, the video will be automatically adjusted to fit the specified dimensions. Horizontal videos will be cropped to match the defined vertical outline of the block, while vertical videos will be rendered as they are within the specified block.&lt;/p&gt;
&lt;p&gt;Next, you can export the video with the configured 9:16 aspect ratio and a given resolution with:&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;// define the MIME type for the video export (MP4 format)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; mimeType&lt;/span&gt;&lt;span&gt; =&lt;/span&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// define a callback function to track the progress of video rendering and encoding&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;renderedFrames&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;=&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;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &apos;Rendered&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// log the number of rendered frames&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 class=&quot;line&quot;&gt;&lt;span&gt;    &apos;frames and encoded&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// log the number of encoded frames&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 class=&quot;line&quot;&gt;&lt;span&gt;    &apos;frames out of&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// log the total frames to be processed&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 class=&quot;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;// define the video export options&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; videoOptions&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;  duration: &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// video duration in seconds&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  framerate: &lt;/span&gt;&lt;span&gt;30&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// video framerate (frames per second)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  targetWidth: &lt;/span&gt;&lt;span&gt;480&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// target video width in pixels (for the resolution)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  targetHeight: &lt;/span&gt;&lt;span&gt;852&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// target video height in pixels (for the resolution)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;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 the page as an MP4 video&lt;/span&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;// the page containing the design video block&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  mimeType, &lt;/span&gt;&lt;span&gt;// the MIME type for the video (MP4)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  progressCallback, &lt;/span&gt;&lt;span&gt;// the callback to track video rendering progress&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  videoOptions &lt;/span&gt;&lt;span&gt;// the options for the video export&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// create an anchor element to trigger the video download&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; anchor&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;anchor.href &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;(blob);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;anchor.download &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;video.mp4&apos;&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;// output video name&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;anchor.&lt;/span&gt;&lt;span&gt;click&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href=&quot;https://img.ly/docs/cesdk/js/export-save-publish/export/overview-9ed3a8/&quot;&gt;&lt;code&gt;exportVideo()&lt;/code&gt;&lt;/a&gt; function produces a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Blob&quot;&gt;&lt;code&gt;blob video file&lt;/code&gt;&lt;/a&gt; of the specified MIME type. Note that the export occurs over multiple iterations of the update loop, with a frame being encoded in each iteration. &lt;/p&gt;
&lt;p&gt;The &lt;code&gt;targetWidth&lt;/code&gt; and &lt;code&gt;targetHeight&lt;/code&gt; options are optional. If used, the video will be resized to fit the target dimensions while maintaining its aspect ratio. This is useful for setting a specific resolution for the output video while preserving the parent block’s width and height on the output video. If omitted, the produced video will match the resolution of the width and height specified with &lt;code&gt;setWidth()&lt;/code&gt; and &lt;code&gt;setHeight()&lt;/code&gt;, respectively.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;progressCallback()&lt;/code&gt; function will track and display the video generation progress in the browser’s console:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&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;295&quot; src=&quot;https://img.ly/_astro/video-generation-output_2ubce0.webp&quot; srcset=&quot;/_astro/video-generation-output_Z1tfnLU.webp 640w, /_astro/video-generation-output_Z1WemnE.webp 750w, /_astro/video-generation-output_Z2n2XVo.webp 828w, /_astro/video-generation-output_2ubce0.webp 1066w&quot;&gt;&lt;/p&gt;
&lt;p&gt;The final lines of the above code snippet trigger the download operation, mimicking the action of clicking a link to download a resource. This is a common JavaScript technique to programmatically trigger a file download.&lt;/p&gt;
&lt;p&gt;Once the export process completes, the output video file (video.mp4) will be automatically downloaded to the browser’s download directory.  Open its properties and this is what you will see:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 404px) 404px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;404&quot; height=&quot;512&quot; src=&quot;https://img.ly/_astro/js-video-generation-file_HN2N0.webp&quot; srcset=&quot;/_astro/js-video-generation-file_HN2N0.webp 404w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Note the video file’s resolution and the fact that its duration is set to 5 seconds, as defined by the &lt;code&gt;duration&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;These options enable you to easily create clips with different lengths, aspect ratios, and resolutions, optimized for various audiences and &lt;a href=&quot;https://img.ly/industries/social-media&quot;&gt;social media platforms like LinkedIn, Instagram, Facebook, X, TikTok, YouTube, and more&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;add-audio-tracks&quot;&gt;Add Audio Tracks&lt;/h3&gt;
&lt;p&gt;Adding an audio track for narration or background music to your clip is straightforward. First, create an &lt;a href=&quot;https://img.ly/docs/cesdk/js/create-video-c41a08/&quot;&gt;“audio” block&lt;/a&gt; and add your audio resource to it:&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; audio&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;audio&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;appendChild&lt;/span&gt;&lt;span&gt;(page, audio);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.&lt;/span&gt;&lt;span&gt;setString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  audio,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;audio/fileURI&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;https://cdn.img.ly/assets/demo/v1/ly.img.audio/audios/far_from_home.m4a&apos;&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 easily adjust the volume, and apply fade-in and fade-out effects 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;// set the volume level to 80%&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.&lt;/span&gt;&lt;span&gt;setVolume&lt;/span&gt;&lt;span&gt;(audio, &lt;/span&gt;&lt;span&gt;0.8&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;// start the audio after 1 second of playback&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.&lt;/span&gt;&lt;span&gt;setTimeOffset&lt;/span&gt;&lt;span&gt;(audio, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// set the audio block&apos;s duration to 5 seconds&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;(audio, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is breakdown of the functions used above:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/docs/cesdk/js/create-video/control-daba54/&quot;&gt;&lt;strong&gt;&lt;code&gt;setVolume()&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt;: Adjusts the audio volume of the block, with a range from 0 (0%) to 1 (100%).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;setTimeOffset()&lt;/code&gt;&lt;/strong&gt;: Sets the time offset of the block relative to its parent. This determines when the block starts playing in the timeline.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;setDuration()&lt;/code&gt;&lt;/strong&gt;: Sets the playback duration of the audio block in seconds, defining how long the block will play during the scene.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this example, the audio starts at the 1-second mark and plays for 5 seconds (from 1s to 6s), with the volume set to 80% of the original.&lt;/p&gt;
&lt;h3 id=&quot;merge-videos&quot;&gt;&lt;strong&gt;Merge Videos&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;To stitch multiple video clips together in CE.SDK, you first need to import and create separate video blocks:&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;// create the first video block&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; video1&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;graphic&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;setShape&lt;/span&gt;&lt;span&gt;(video1, engine.block.&lt;/span&gt;&lt;span&gt;createShape&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;rect&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; videoFill1&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;createFill&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;video&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;setString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  videoFill1,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;fill/video/fileURI&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;https://cdn.img.ly/assets/demo/v2/ly.img.video/videos/pexels-drone-footage-of-a-surfer-barrelling-a-wave-12715991.mp4&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;engine.block.&lt;/span&gt;&lt;span&gt;setFill&lt;/span&gt;&lt;span&gt;(video1, videoFill1);&lt;/span&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 the second video block&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; video2&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;graphic&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;setShape&lt;/span&gt;&lt;span&gt;(video2, engine.block.&lt;/span&gt;&lt;span&gt;createShape&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;rect&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; videoFill2&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;createFill&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;video&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;setString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  videoFill2,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;fill/video/fileURI&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;https://cdn.img.ly/assets/demo/v2/ly.img.video/videos/pexels-kampus-production-8154913.mp4&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;engine.block.&lt;/span&gt;&lt;span&gt;setFill&lt;/span&gt;&lt;span&gt;(video2, videoFill2);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, you have two different video clips loaded from separate video files.&lt;/p&gt;
&lt;p&gt;Now, assume you want to produce a 10-second video that includes both clips. Start by setting the duration of the page block to 10 seconds with the &lt;a href=&quot;https://img.ly/docs/cesdk/js/create-video/control-daba54/&quot;&gt;&lt;code&gt;setDuration()&lt;/code&gt;&lt;/a&gt; method:&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;engine.block.&lt;/span&gt;&lt;span&gt;setDuration&lt;/span&gt;&lt;span&gt;(page, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, suppose you want the first clip (video1) to play for 7 seconds, and the second clip (video2) to fade in after that and play for 3 seconds (from second 1 to second 4 of the original second clip). This is how you can set that up:&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;// set the duration of the first and second video clips&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.setDuration(video1, 7); // video1 plays for 7 seconds&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.setDuration(video2, 3); // video2 plays for 3 seconds&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// wait for the second video to be fully loaded before applying the trim options&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;await engine.block.forceLoadAVResource(videoFill2);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// trim the second video to start at 1 second and play up to second 4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.setTrimOffset(videoFill2, 1);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.setTrimLength(videoFill2, 4);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// add a fade-in animation to the second video&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const fadeInAnimation = engine.block.createAnimation(&quot;fade&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.setInAnimation(video2, fadeInAnimation);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are the methods used above:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;setDuration()&lt;/code&gt;&lt;/strong&gt;: Defines the duration (in seconds) that a video block will be active during playback.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;**forceLoadAVResource()**&lt;/code&gt;: Ensures that the audio or video resource (in this case, &lt;code&gt;video2&lt;/code&gt;) is fully loaded before applying trim operations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;setTrimOffset()&lt;/code&gt;&lt;/strong&gt;: Sets the start point (in seconds) within the video where playback begins.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;setTrimLength()&lt;/code&gt;&lt;/strong&gt;: Defines the length of the video to be played from the trim offset.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/docs/cesdk/js/animation/overview-6a2ef2/&quot;&gt;&lt;strong&gt;&lt;code&gt;createAnimation()&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt;: Creates a fade-in animation for the second video (&lt;code&gt;video2&lt;/code&gt;) to smoothly transition into the clip.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, add both video blocks to the track block for playback:&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 track = engine.block.create(&quot;track&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.appendChild(page, track);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.appendChild(track, video1); // add the first video to the track&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.appendChild(track, video2); // add the second video to the track&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.fillParent(track);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result will be:&lt;/p&gt;
&lt;figure class=&quot;kg-card kg-embed-card&quot;&gt;&lt;iframe width=&quot;225&quot; height=&quot;400&quot; src=&quot;https://blog.img.ly/2025/01/video-1.mp4&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;&lt;/figure&gt;
&lt;p&gt;Note how the second clip fades in after 7 seconds of the first clip, lasting for 3 seconds as specified.&lt;/p&gt;
&lt;h3 id=&quot;incorporate-text-variables&quot;&gt;&lt;a href=&quot;https://img.ly/blog/how-to-video-generation-with-javascript//#Incorporate-Text-Variables&quot;&gt;&lt;/a&gt;Incorporate Text Variables&lt;/h3&gt;
&lt;p&gt;Now, suppose you want to display cool words or messages in your videos. You can easily achieve this by adding a &lt;a href=&quot;https://img.ly/docs/cesdk/js/text/edit-c5106b/&quot;&gt;&lt;code&gt;&quot;text&quot;&lt;/code&gt; block&lt;/a&gt; to your page, as shown 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;const text = engine.block.create(&quot;text&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// set the vluae of the text variable&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.replaceText(text, &quot;Surfing\nis\nCOOL!&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// set the text color to white and semi-transparent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.setTextColor(text, { r: 255.0, g: 255.0, b: 255.0, a: 0.8 });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// set the font size for the text&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.setTextFontSize(text, 30);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// positioning the text on an absolute position on the video&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.setWidthMode(text, &quot;Auto&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.setHeightMode(text, &quot;Auto&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.setPositionX(text, 130);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.setPositionY(text, 800);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// append the text block to the page&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;engine.block.appendChild(page, text);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The video canvas will now display a white “Surfing is COOL!” message as follows:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 514px) 514px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;514&quot; height=&quot;812&quot; src=&quot;https://img.ly/_astro/js-video-generation-surfing_ro7yG.webp&quot; srcset=&quot;/_astro/js-video-generation-surfing_ro7yG.webp 514w&quot;&gt;&lt;/p&gt;
&lt;p&gt;In this example, the content of the text block is static. However, it can easily be retrieved from a database or any other source for programmatic video generation with different messages, including personalized ones for tailored outreach (e.g., for custom greetings or offers).&lt;/p&gt;
&lt;h2 id=&quot;advanced-use-case-prompt-based-video-generation-using-ai&quot;&gt;&lt;a href=&quot;https://img.ly/blog/how-to-video-generation-with-javascript//#Advanced-Use-Case-Prompt-Based-Video-Generation-Using-AI&quot;&gt;&lt;/a&gt;Advanced Use Case: Prompt-Based Video Generation Using AI&lt;/h2&gt;
&lt;p&gt;As highlighted in our &lt;a href=&quot;https://www.linkedin.com/posts/eray-basar-57684711_imagine-building-a-full-video-app-in-a-single-activity-7262806525797089280-S_L8?utm_source=share&amp;#x26;utm_medium=member_desktop&quot;&gt;recent LinkedIn post&lt;/a&gt;, &lt;a href=&quot;https://IMG.LY&quot;&gt;IMG.LY&lt;/a&gt; has just experimented with an MVP that turns keywords into fully edited short videos, complete with a script, voiceover, and visuals—all automated.&lt;/p&gt;
&lt;p&gt;That is made possible by integrating CE.SDK with an LLM for coding and scriptwriting, &lt;a href=&quot;https://elevenlabs.io/&quot;&gt;ElevenLabs&lt;/a&gt; for voice synthesis, and Flux (via &lt;a href=&quot;https://fal.ai/&quot;&gt;fal.ai&lt;/a&gt;) for visuals. Specifically, the LLM uses CE.SDK for video composition, animation, and rendering.&lt;/p&gt;
&lt;p&gt;This is just the beginning, showing how AI can be combined with CE.SDK to programmatically generate video scripts—or even templates. A possible scenario would be to use the &lt;a href=&quot;https://img.ly/docs/cesdk/js/user-interface/ui-extensions-d194d1/&quot;&gt;Creative Editor SDK plugin API&lt;/a&gt; to extend CE.SDK with custom plugins that adds AI directly to the design editor engine.&lt;/p&gt;
&lt;p&gt;For example, you could develop a plugin with an LLM integration that allows users to input a prompt like: &lt;em&gt;“Create a 15-second video with the text ‘Welcome to our App!’ and upbeat background music.”&lt;/em&gt; The LLM will process the request, using Creative Editor SDK to automate video creation by leveraging its powerful features.&lt;/p&gt;
&lt;p&gt;Alternatively, the AI could produce a video that you can then automatically edit and enhance using CE.SDK capabilities. One thing is certain: the possibilities are endless with CE.SDK + AI!&lt;/p&gt;
&lt;h2 id=&quot;conclusion-the-future-of-programmatic-video-generation&quot;&gt;&lt;a href=&quot;https://img.ly/blog/how-to-video-generation-with-javascript//#Conclusion-The-Future-of-Programmatic-Video-Generation&quot;&gt;&lt;/a&gt;Conclusion: The Future of Programmatic Video Generation&lt;/h2&gt;
&lt;p&gt;CE.SDK for JavaScript offers exceptional versatility in automating video workflows, allowing you to easily generate, edit, and render videos with just a few lines of code. You can stitch multiple video files, add background audio tracks, and incorporate dynamic text variables to finally produce videos in the browser—all thanks to the CE.SDK headless engine.&lt;/p&gt;
&lt;p&gt;Looking ahead, AI integration with CE.SDK is unlocking even more powerful possibilities, such as prompt-based video content creation and automatic editing directly in the browser.&lt;/p&gt;
&lt;p&gt;With the headless engine provided by CreativeEditor SDK, programmatic video generation in JavaScript can be implemented in just a few minutes. Explore the video capabilities of CE.SDK and &lt;a href=&quot;https://img.ly/docs/cesdk/js/get-started/overview-e18f40/&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><dc:creator>Jan</dc:creator><media:content url="https://blog.img.ly/2025/01/javascript-generate-video.jpg" medium="image"/><category>Video Editing</category><category>Creative Automation</category><category>CE.SDK</category></item><item><title>Javascript Video Editing: Ultimate Guide for Developers and PMs</title><link>https://img.ly/blog/javascript-video-editing-guide/</link><guid isPermaLink="true">https://img.ly/blog/javascript-video-editing-guide/</guid><description>A comprehensive guide to JavaScript video editing for developers and PMs. Learn about essential features, modern web technologies, and common tools like FFmpeg.js and IMG.LY&apos;s CE.SDK to create powerful web-based video editors.</description><pubDate>Fri, 20 Dec 2024 14:06:55 GMT</pubDate><content:encoded>&lt;p&gt;When developing or integrating a JavaScript-based video editor for the web, you must consider a number of factors to ensure the solution is both efficient and robust. This post is the definitive guide for anyone embarking on such a project. It explores the key technologies involved, their strengths and weaknesses, and how different use cases influence the choice of tech stack. We’ll examine diverse use cases, from lightweight, browser-based editors for quick edits to more advanced tools requiring complex processing and rendering. We’ll then discuss how these scenarios drive the selection of features and technology.&lt;/p&gt;
&lt;p&gt;Additionally, we will show you the best open-source solutions that can accelerate development. This technical analysis will help you make an informed “build vs. buy” decision, ensuring you select the right approach for your project. Throughout this post, we will use the example of the IMG.LY’s JavaScript video editor (see &lt;a href=&quot;https://img.ly/showcases/cesdk/video-ui/web&quot;&gt;here for a demo&lt;/a&gt;), showing you how these considerations shaped its architecture and feature set. You will learn practical insights into the decision-making process behind a successful web-based video editor.&lt;/p&gt;
&lt;p&gt;Modern web technologies like WebGL, WebCodecs and WebAssembly have enabled browsers to efficiently perform resource-intensive tasks such as video editing that had hitherto been the sole domain of desktop applications. As a result Javascript based video editing tools such as &lt;a href=&quot;https://www.veed.io/&quot;&gt;veed.io&lt;/a&gt; have increased in popularity and users expects ever more sophisticated video editing capabilities inside modern video based web apps.&lt;/p&gt;
&lt;h2 id=&quot;video-editing-use-cases&quot;&gt;Video Editing Use Cases&lt;/h2&gt;
&lt;p&gt;Let’s first outline use cases and requirements of video editing applications on the web. These will inform our discussion of technical features of respective solutions below and grant a conceptual framework for evaluating potential benefits and drawbacks of each solution.&lt;/p&gt;
&lt;h3 id=&quot;simple-video-editing&quot;&gt;Simple Video Editing&lt;/h3&gt;
&lt;p&gt;Let’s start with the base case, &lt;strong&gt;simple video edits&lt;/strong&gt; including trimming, cropping and resizing and simple effects such as adjusting brightness or saturation. This is usually sufficient feature set to support video editing for the following use cases:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sales Outreach Videos:&lt;/strong&gt; Sales teams often need to quickly edit customer-specific videos to personalize their outreach. This may involve trimming irrelevant portions, adding company logos, or adjusting brightness to ensure visual clarity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Messaging Applications:&lt;/strong&gt; These often require basic editing tools to allow users to crop, trim, or apply simple filters to shared videos, ensuring they’re concise and visually appealing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Screencasting:&lt;/strong&gt; Screencasting tools benefit from trimming and resizing capabilities to focus on key parts of recorded screens. Adding effects like brightness adjustment can make tutorials clearer and more professional.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CMS Systems:&lt;/strong&gt; Content management systems may offer built-in video editing to help users optimize media assets for specific platform requirements, such as resizing for web embeds or adding subtle branding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Screen Recording Applications:&lt;/strong&gt; Screen recording applications often include simple editing options for cleaning up recorded content by trimming extraneous sections or cropping to highlight the most relevant parts.&lt;/p&gt;
&lt;h3 id=&quot;video-annotation&quot;&gt;Video Annotation&lt;/h3&gt;
&lt;p&gt;Next, users might want to add an additional layer of information to videos and overlay other assets, such as stickers, shapes, overlays, or text. Crucially these assets need to be time-based—that is, users need control over when the asset is shown so that particular video sequences can be referenced. This introduces the need for a timeline to arrange different video components relative to each other in time, as well as features like voice-over and audio track support.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;E-commerce Reviews:&lt;/strong&gt; Sellers and reviewers can annotate product demo videos with callouts, price tags, and feature highlights to make the content more engaging and informative.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Claims Management / Insurance:&lt;/strong&gt; Insurance companies can use annotation to highlight key details in submitted video claims, such as timestamps of damage or explanatory text overlaying critical sections of footage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real Estate:&lt;/strong&gt; Realtors might annotate property walkthroughs by adding labels, dimensions, or descriptive text overlays to highlight key features of the home or property.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Educational Applications:&lt;/strong&gt; Instructors can use annotation tools to emphasize key moments in lectures or tutorials, such as overlaying text with formulas or concepts, or adding visual shapes to guide attention.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Productivity Tools:&lt;/strong&gt; Users can annotate meeting recordings with timestamps, text notes, or overlay diagrams to summarize key decisions or action points.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Healthcare and Telemedicine:&lt;/strong&gt; Medical professionals might annotate diagnostic videos or procedure recordings to explain findings or highlight areas of interest for training purposes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Customer Support &amp;#x26; Onboarding Tools:&lt;/strong&gt; Companies can add annotations to video tutorials or troubleshooting guides to direct users through specific steps or highlight important information.&lt;/p&gt;
&lt;h3 id=&quot;video-composition&quot;&gt;Video Composition&lt;/h3&gt;
&lt;p&gt;As we’re moving away from single media editing to creating video compositions of several types of media such as audio tracks, text, images, animations, and effects to create appealing visual designs, a well-designed timeline becomes even more important. Users need to manage many different video components in time, requiring user-friendly ways to browse and integrate external assets. This editor variant is most relevant for the following use cases:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Marketing Tech (Promotional Videos):&lt;/strong&gt; Marketers can create visually stunning promotional videos by combining custom animations, background music, and text overlays for branding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Social Media (Stories and Reels):&lt;/strong&gt; Social media creators can quickly craft short, engaging videos that combine multiple assets like stickers, animations, and dynamic transitions tailored to platform-specific formats.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Event Highlight Reels:&lt;/strong&gt; Event organizers can compile videos, photos, and music into cohesive highlight reels that encapsulate the essence of the occasion.&lt;/p&gt;
&lt;h3 id=&quot;template-based-video-creation&quot;&gt;Template-based Video Creation&lt;/h3&gt;
&lt;p&gt;For many applications, users need starting points and examples for their designs. Starting from a blank canvas is rarely necessary for most use cases. Take a simple product video that includes promotional text, a brand logo, and animations. There is no need to reinvent the wheel for these types of videos. Instead, design applications should offer template libraries to accelerate user workflows.&lt;/p&gt;
&lt;p&gt;Once we introduce workflows involving several stakeholders—such as designers setting up a certain brand framework and marketers working with and adapting these preconfigured designs—templates need to be able to constrain what edit operations adaptors can perform. These requirements are particularly important for these use cases:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Digital Asset Management:&lt;/strong&gt; Organizations can manage and deploy branded video templates across teams, ensuring consistent design and messaging.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Social Media Publishing:&lt;/strong&gt; Social media managers can utilize templates for quick turnaround on platform-specific video formats, such as Instagram Stories or LinkedIn posts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Marketing Tech:&lt;/strong&gt; Marketing teams can rely on pre-designed templates to churn out campaign videos at scale while maintaining brand consistency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Training Videos:&lt;/strong&gt; HR or L&amp;#x26;D departments can adapt existing templates to quickly produce training materials customized for different teams or scenarios.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;IMG.LY&amp;#39;s Video Editor enables template based constraints&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1380px) 1380px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1380&quot; height=&quot;789&quot; src=&quot;https://img.ly/_astro/Video-Templating_1eQR66.webp&quot; srcset=&quot;/_astro/Video-Templating_7I73m.webp 640w, /_astro/Video-Templating_1h8MhA.webp 750w, /_astro/Video-Templating_1R3Qif.webp 828w, /_astro/Video-Templating_Z2oAeix.webp 1080w, /_astro/Video-Templating_ZYbYlz.webp 1280w, /_astro/Video-Templating_1eQR66.webp 1380w&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;creative-automation-for-video&quot;&gt;Creative Automation for Video&lt;/h3&gt;
&lt;p&gt;Finally, you can leverage data and automation to generate variations of your templates at scale, significantly boosting the productivity of use cases where users need to either test a large number of designs, publish to different channels with different requirements, or adapt designs to many slightly different instances, for example, menu designs for a franchise.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Marketing Tech:&lt;/strong&gt; Marketers can generate hundreds of ad variations for A/B testing or localization by dynamically adjusting text, visuals, or calls to action.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Social Media Publishing:&lt;/strong&gt; Social media teams can automate the creation of videos tailored to platform specifications, such as aspect ratios or resolution, while retaining core branding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;E-commerce:&lt;/strong&gt; Retailers can produce personalized product showcase videos for different customer segments, featuring tailored offers, pricing, or recommendations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hospitality Industry:&lt;/strong&gt; Hotels and restaurants can dynamically generate location-specific promotional videos showcasing seasonal offers, menus, or events.&lt;/p&gt;
&lt;h2 id=&quot;essential-video-editing-features&quot;&gt;Essential Video Editing Features&lt;/h2&gt;
&lt;p&gt;The above use cases are enabled by a set of features from simple transforms to complex compositions. Some video editors are focused more on manipulating individual video clips while others are more oriented towards video creation providing fully-fledged multi-media composition tool. This concise overview serves as a reference for evaluating video editing solutions:&lt;br&gt;
Transforms&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cut, Trim, and Split&lt;/strong&gt;: Basic editing tools for segmenting video clips.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resize and Scale&lt;/strong&gt;: Adjusts clip dimensions, ensuring proper fit or aspect ratio.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Crop and Rotate&lt;/strong&gt;: Controls framing and orientation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zooming Capabilities&lt;/strong&gt;: A UX feature enabling detailed editing and close-up views for precision.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;adjustments&quot;&gt;Adjustments&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Basic Adjustments&lt;/strong&gt;: Controls brightness, contrast, and other visual enhancements.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Filters and Effects&lt;/strong&gt;: Sets the visual appearance and atmosphere of videos.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audio Tracks and Mixing&lt;/strong&gt;: Supports multi-track audio adjustments and volume balancing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Text and Overlay Options&lt;/strong&gt;: Allows visual enhancements with text, captions, and emojis, providing opacity and layering options.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;composition&quot;&gt;Composition&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multi-media Composition&lt;/strong&gt;: Overlaying images, stickers, text as well as audio tracks are essential for creating videos through composition.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-Track Editing&lt;/strong&gt;: Elements and layers need to be position relative to each other in time allowing the creation of complex composition.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Canvas-Based Editing&lt;/strong&gt;: Provides a flexible workspace for positioning and layering media elements.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Timeline Management&lt;/strong&gt;: Split, join and arrange clips on a timeline. Manages the positioning of clips and timing for seamless transitions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Animations&lt;/strong&gt;: Make static elements dynamic and control their behavior in time. Can be use to create videos from scratch and enhance the storytelling workflow of existing ones.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;IMG.LY&amp;#39;s Video Editor Timeline Feature&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1376px) 1376px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1376&quot; height=&quot;960&quot; src=&quot;https://img.ly/_astro/Video-Editor-Timeline_Z1nLaxM.webp&quot; srcset=&quot;/_astro/Video-Editor-Timeline_Z2giwjW.webp 640w, /_astro/Video-Editor-Timeline_ZYWIGq.webp 750w, /_astro/Video-Editor-Timeline_1NmLoH.webp 828w, /_astro/Video-Editor-Timeline_Z23wuov.webp 1080w, /_astro/Video-Editor-Timeline_Z2pwGVX.webp 1280w, /_astro/Video-Editor-Timeline_Z1nLaxM.webp 1376w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-technology-landscape&quot;&gt;The Technology Landscape&lt;/h2&gt;
&lt;p&gt;Some time in the early 2000s the web started to be viewed as a platform to run applications not just a distributed database to deliver static html documents. While Javascript development was not yet at a point to serve as foundation for the kind of application development we see today, Adobe Flash filled the role of development platform. Video editing in the early days of the web was mainly provided by Flash-based tools involving simple transform operations such as trimming and basic transitions. Java applets were another way to deliver processing intensive programs via the browser while executing them outside of the browser inside the JVM. These early attempts were either too clunky, since they could not run natively in the browser or lacked the performance to be useful for high quality editing.&lt;/p&gt;
&lt;p&gt;When HTML5 came onto the scene in the early 2000s it became possible to handle multimedia content natively in the browser. Coupled with significant performance advances of Javascript and the introduction of WebRTC this opened the door for video editing on the web.&lt;/p&gt;
&lt;p&gt;Some early cloud-based editors, such as WeVideo and Magisto, while more convenient and user-friendly than its predecessors still suffered from performance issues due to latency and lacked the depth of features found in desktop software.&lt;/p&gt;
&lt;p&gt;In the past few years advances such as WebAssembly and Javascript libraries built upon it e.g. ffmpeg.js have revolutionised browser-based video editing. User can now perform complex editing tasks like multi-track timelines, video effects and real-time in-browser rendering. Modern web-based solution have even come to rival desktop apps, because they can take advantage of GPU acceleration, modern APIs like WebGL and UI frameworks such as React.&lt;/p&gt;
&lt;p&gt;Let’s have a look at the state of the art web technologies that allow us to create performant video editing experiences inside the browsers. We are going to explore their relative strength, their complexities and conclude with when to best use each of these technologies:&lt;/p&gt;
&lt;h3 id=&quot;canvas-api&quot;&gt;Canvas API&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API&quot;&gt;HTML5 Canvas API&lt;/a&gt; should be most familiar to most modern web developers, since it’s a familiar and popular way to render 2D graphics in the browser. It’s use for video editing is limited and restricted to simple overlays, trimming, composition and animation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Complexity:&lt;/strong&gt; Canvas is the least complex of the technologies introduced below and overlaps with the WebGL feature set to some extend, it’s API is well-known to most web developers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; Canvas is lightweight, compatible across all browsers, and easy to implement, making it a great choice for simple use cases that require basic effects and no timeline.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Downsides:&lt;/strong&gt; While well suited for simpler tasks Canvas lacks the GPU-acceleration of WebGL, hence it is not recommended for resource intensive operation or performance sensitive use cases.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When to Use:&lt;/strong&gt; Choose Canvas for simple editing tasks, like trimming or basic overlays, or if you need a fallback technology that works well on legacy browsers and devices.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;webgl&quot;&gt;WebGL&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API&quot;&gt;WebGL&lt;/a&gt; opens up the power of GPU-accelerated graphics to the browser, making it the default choice for any use case requiring high performance rendering. As such modern Javascript video editing solution are well served by webGL when looking to render visual effects in real time, add transitions or facilitate multi-layer compositions by leveraging the GPU.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Complexity:&lt;/strong&gt; WebGL comes with a steep learning curve especially if you are unfamiliar with 3D graphics processing, shaders and GPU programming.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; WebGL delivers high performance for processing complex video effects and can handle significant volume of data without latency. That makes it ideal for advanced, highly responsive video editing features.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Downsides:&lt;/strong&gt; It can be challenging to engineer modular and maintainable WebGL code, debugging shaders and ensuring consistent performance across devices is an art in itself. WebGL might also be overpowered for simple editing use cases that do not justify the added complexity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When to Use:&lt;/strong&gt; Use WebGL if you’re creating a video editor with high-performance demands, particularly if real-time effects, transitions, or multi-layer compositing are needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;webcodecs-api&quot;&gt;WebCodecs API&lt;/h3&gt;
&lt;p&gt;Next up in our arsenal of formidable web technologies is a recent addition to modern browsers, WebCodecs, provide direct access to hardware accelerated video encoding and decoding.&lt;/p&gt;
&lt;p&gt;The WebCodecs API is a recent addition to the browser, allowing direct access to hardware-accelerated video decoding and encoding. This API makes it easier to handle high-resolution video without delays, thanks to efficient decoding directly in the browser.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Complexity:&lt;/strong&gt; WebCodecs is relatively easy to use for those familiar with media formats. However, you may need other technologies for rendering and complex editing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; This API offers quick access to hardware acceleration, ideal for managing large video files. With it, you can efficiently handle high-definition video playback and streaming.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Downsides:&lt;/strong&gt; Browser support for WebCodecs is still expanding, so compatibility might be a concern. Additionally, WebCodecs alone doesn’t provide a full editing suite; it needs to be combined with rendering and compositing technologies.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When to Use:&lt;/strong&gt; WebCodecs is perfect if you need efficient decoding and encoding, especially for handling high-definition playback or live streaming.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;webassembly-wasm&quot;&gt;WebAssembly (Wasm)&lt;/h3&gt;
&lt;p&gt;The next performance frontier browser pushed towards was enabling near-native performance by compiling languages like C++ or Rust into a format that efficiently runs in Javascript. This format is &lt;a href=&quot;https://webassembly.org/&quot;&gt;WebAssembly&lt;/a&gt;, which makes it possible to handle computationally expensive tasks like encoding, decoding or frame manipulation inside the browser.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Complexity:&lt;/strong&gt; Working with WebAssembly is significantly more complex that the technologies discussed above. Developers need knoweldge of C++ or Rust as well as experience compiling to Wasm.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; WebAssembly enables browsers to handle complex video processing tasks, such as encoding and decoding, with near-native performance. Libraries such as FFmpeg build upon WebAssembly to make familiar video tools available to web developers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Downsides:&lt;/strong&gt; Development with Wasm is complex, and debugging can be a challenge. Additionally, Wasm modules may increase load times.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When to Use:&lt;/strong&gt; Use WebAssembly when building native libraries for complex operation and expensive computations, such as encoding and decoding.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our article o&lt;a href=&quot;https://img.ly/blog/how-to-build-a-video-editor-with-wasm-in-react/&quot;&gt;n building a video editor with React and Wasm&lt;/a&gt; gives you a real-world starting point for your own apps.&lt;/p&gt;
&lt;h3 id=&quot;mediastream-api&quot;&gt;MediaStream API&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/MediaStream&quot;&gt;MediaStream API&lt;/a&gt; is essential for gaining easy access to live video sources from within the browser, such as webcams. If your video editing app includes a feature for real-time recording or streaming the MediaStream API is an essential tool.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Complexity:&lt;/strong&gt; MediaStream exposes a fairly simple API and is relatively easy to learn for experienced web developers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; This API is well-suited for video feeds with low latency such as recording or streaming and does require addition encoding or decoding.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Downsides:&lt;/strong&gt; Any use case requiring post-production video has to rely on additional technologies or frameworks, as the MediaStream API is limited to live feeds. The quality of the stream depends on the input source and can vary.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When to Use:&lt;/strong&gt; Use MediaStream for applications with live recording or streaming functionality.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;web-audio-api&quot;&gt;Web Audio API&lt;/h3&gt;
&lt;p&gt;The final web technology essential for video editing is the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API&quot;&gt;Web Audio API&lt;/a&gt;. Whether you want to add effects or mix multiple audio tracks, this API offers sophisticated tools for audio manipulation.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Complexity:&lt;/strong&gt; For developers unfamiliar with audio processing the learning curve can be stepp, however the API is well designed and there is a host of educational resources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; Web Audio API allows precise control over audio enabling your app to offer audio adjustments such as effects or reverb and multi-track editing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Downsides:&lt;/strong&gt; It can be challenging to accurately synchronize audio with video, especially if your app includes a real-time component.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When to Use:&lt;/strong&gt; Opt for Web Audio API when audio editing is a core feature of your video editor.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;using-these-technologies-together&quot;&gt;Using These Technologies Together&lt;/h3&gt;
&lt;p&gt;Together, these technologies can be use to build a comprehensive browser based editor that is virtually indistinguishable from its desktop based counterparts. Different parts of the video editing workflow can be handled by each technology, while some choices have to made depending on the complexity and requirements of your projects, e.g. whether you’ll need resource intensive processing or an integration will real-time sources.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Video Decoding&lt;/strong&gt; - WebCodecs API (for efficient video loading and playback).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rendering&lt;/strong&gt; - WebGL (for high-performance effects) and Canvas API (for simpler editing features).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Complex Processing&lt;/strong&gt; - WebAssembly (for encoding/decoding and using native libraries like FFmpeg).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audio Processing&lt;/strong&gt; - Web Audio API (for mixing and adding effects to soundtracks).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Live Recording&lt;/strong&gt; - MediaStream API (to capture live video from devices).&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;open-source-javascript-video-editing-libraries&quot;&gt;Open Source Javascript Video Editing Libraries&lt;/h2&gt;
&lt;p&gt;Fortunately in many cases someone has already done the heavy lifting and abstracted those browser APIs into simple to use libraries and frameworks. We’ll explore the most prominent ones distinguishing between projects that are for processing raw video and those that already provide an API for you to use. It is important to note that the libraries introduced below do not provide a video editing UI, unfortunately there are no open source video editors offering an end user interface that meet the standards for inclusion in a production grade application. None of the projects we evaluated met the demands we place on third-party libraries with respect to robustness, stability, maintenance and future development. However many can provide good starting points and references on how to build your own UI such as &lt;a href=&quot;https://github.com/mifi/reactive-video&quot;&gt;Reactive Video&lt;/a&gt; for React UIs.&lt;/p&gt;
&lt;h3 id=&quot;ffmpegjs&quot;&gt;FFmpeg.js&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/ffmpegwasm/ffmpeg.wasm&quot;&gt;FFmpeg.js&lt;/a&gt; is the JavaScript port of the popular FFmpeg library compiled into WebAssembly. It enables developers to manipulate and process raw video files entirely in the browser. You can trim, convert formats, extract audio, and apply filters to videos without needing external software. However, FFmpeg just gives you the raw toolchain for building video editing features, you are still tasked with building the entire UX and editing workflows. We have an extensive guide on FFmpeg that you can consult to explore its capabilities and get started learning FFmpeg syntax.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Most comprehensive library for web based video and audio processing.&lt;/li&gt;
&lt;li&gt;Fully client-side, no need for server-side processing.&lt;/li&gt;
&lt;li&gt;Highly flexible, allowing fine-grained control over video workflows.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Downsides:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Fairly complex for beginners with a steep learning curve, requires familiarity with FFmpeg’s command-line syntax.&lt;/li&gt;
&lt;li&gt;FFmpeg is licensed under LGPL which makes it ill-suited for most commercial projects.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;**When to Use:**FFmpeg.js is best suited for projects with custom requirements and UI making it necessary to exert fine-grained control over video processing tasks, especially for use cases like format conversion, advanced editing pipelines, or serverless video workflows.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following two libraries allow you to programatically create and edit videos, we focus on the most stable, popular libraries here, while there might be promising up and comers we want to ensure a degree of dependability of the third-party code to be used in production systems.&lt;/p&gt;
&lt;h3 id=&quot;remotion&quot;&gt;Remotion&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.remotion.dev/&quot;&gt;Remotion&lt;/a&gt; is a React-based framework that enables developers to edit or create videos programmatically in a declarative fashion using React components. React’s declarative syntax makes it comparatively simple to manage video content and Remotion provides a convenient UI for previewing and editing videos. While its studio UI cannot be embedded directly, Remotion can serve as the graphics processing engine for custom UIs. Although it is a commercial project, it offers a generous free tier for individuals and small businesses.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Leverages React’s declarative model for seamless video creation.&lt;/li&gt;
&lt;li&gt;Provides robust video preview capabilities for iterative development.&lt;/li&gt;
&lt;li&gt;Flexible integration with custom-built UIs.&lt;/li&gt;
&lt;li&gt;Generous free tier for non-commercial and small-scale projects.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Downsides:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;The built-in studio UI cannot be embedded, requiring developers to create their own UI for embedding.&lt;/li&gt;
&lt;li&gt;React-specific, limiting use in non-React environments.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;**When to Use:**Use Remotion when building React-based video applications that require programmatic video generation or dynamic video content. It’s ideal for teams already familiar with React and for applications where embedding is not a core requirement.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;etrojs&quot;&gt;&lt;strong&gt;Etro.js&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Similarly to Remotion, Etro.js is a JavaScript framework for programmatic video editing, it is framework agnostic and provides an API for simple composition of layers, effects and exporting videos.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Includes support for advanced effects using GLSL shaders.&lt;/li&gt;
&lt;li&gt;Designed for in-browser video workflows.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Downsides:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Minimal community support compared to larger projects.&lt;/li&gt;
&lt;li&gt;Requires additional effort to create a full UI.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When to Use:&lt;/strong&gt; Suitable for applications where programmatic control and customization are key.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;extensibility&quot;&gt;Extensibility&lt;/h2&gt;
&lt;p&gt;An important consideration when deciding to buy a prebuilt video editing solution is the degree of customization and extensibility required by your use case versus how critical speed to market is. While most SDKs provide enough flexibility by exposing cor API to build a solution that best suits your needs, this level of technical control comes at the expense of higher development and maintenance efforts. White-label solutions on the other end of the spectrum offer limited customization often relying on iFrames for embedding while providing maximum speed to market. If you are still in the phase where you need to make the business case for video editing features these might be key to allowing to quickly iterate. To learn more about weighing the pros and cons of SDK vs. White-label explore our &lt;a href=&quot;https://img.ly/blog/sdk-vs-white-label-consider-these-differences-when-you-choose-a-solution/&quot;&gt;extensive guide on the topic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;IMG.LY’s CE.SDK achieves extensibility by providing an &lt;a href=&quot;https://img.ly/blog/img-ly-sdk-plugin-system/&quot;&gt;interface for plugins&lt;/a&gt; that allow developers to add custom features and UI elements.&lt;/p&gt;
&lt;h2 id=&quot;cross-platform-video-editing&quot;&gt;Cross Platform Video Editing&lt;/h2&gt;
&lt;p&gt;While this post is dedicated specifically to Javascript video editing an important consideration for choosing a video editing tech stack is whether there exists an intermediary output format that preserves editing operations so that videos can be edited on other devices and platforms. This is important for collaborative video editing use cases as well as use cases where videos are captured and basic edits performed on mobile and the finishing touches performed on the desktop.&lt;br&gt;
These consideration are particular pertinent for the Real Estate, Telemedicine and Productivity use cases described above, since they involve either a multi-device or collaborative component.&lt;/p&gt;
&lt;p&gt;At most common libraries or SDKs can achieve that by offering support for some form of serialization, that is exporting the edit operations along with the unedited video to then reapply edits upon import.&lt;/p&gt;
&lt;p&gt;IMG.LY’s CE.SDK, on the other hand, is the only video editing SDK that is truly cross-platform. That means it is built atop a single creative engine that is portable to any platform. Whether iOS, Android, Desktop, or the Web, every platform uses the same underlying tech and uses the same custom format for scenes which contain all assets and edits making error-prone serializations obsolete.&lt;/p&gt;
&lt;p&gt;Furthermore:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;iOS and Android make use of the same underlying API.&lt;/li&gt;
&lt;li&gt;Cross platform feature parity is baked into the cake. Since core functionality is implemented at the engine level, features are guaranteed to be available on both platform, although the timeline might differ somewhat.&lt;/li&gt;
&lt;li&gt;Designs are 100% interoperable and consistent between platforms. Exporting and importing design files across platforms works seamlessly and final renderings are guaranteed to be consistent.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;javascript-video-editing--generative-ai&quot;&gt;Javascript Video Editing &amp;#x26; Generative AI&lt;/h2&gt;
&lt;p&gt;Generative AI is impacting video editing and generation across the board. Video generation obvious application of generative AI, but AI is also changing the way we we compose, create an edit videos in more subtle ways.&lt;/p&gt;
&lt;p&gt;Features such as automated transitions, color grading, voice enhancement, voice over generation, background removal or inpainting are&lt;/p&gt;
&lt;p&gt;Currently, these capabilities are offered by multiple specialized providers, such as ElevenLabs and OpenAI’s Sora. Firstly, that means modern Javascript video editors need to be open enough to serve as integrators for various, programmatically combining voice tracks, video clips, text, audio and other media before presenting users with an editing UI. Secondly, traditional video editing needs to enable the human in the loop. AI is still imperfect and manually refining unchangeable artifacts through prompts is inefficient and tedious. Instead the output by AI must remain editable and any editor solution smoothly interface with those artifacts to allow users to add the necessary polish, swap out media and regenernate individual parts of the scene.&lt;/p&gt;
&lt;p&gt;To explore what this might look like in practice have a look at our CEO Eray Basar’s latest project building an AI video generator on top of &lt;a href=&quot;https://img.ly/products/creative-sdk&quot;&gt;CE.SDK&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When building or integrating a JavaScript video editor, developers and PMs have to navigate a complex technology landscape and make myriad decisions along the way. This guide has covered everything from essential use cases and feature sets to the cutting-edge technologies that underly browser-based video editing. Whether your project requires basic trimming tools or advanced multimedia composition, understanding the benefits and downsides of the various solutions like Canvas, WebGL, WebCodecs, WebAssembly, and more is crucial for choosing the right stack.&lt;/p&gt;
&lt;p&gt;We’ve also explored how open-source tools like FFmpeg.js, Remotion, and Etro.js can accelerate development, while SDKs like IMG.LY’s CE.SDK offer unmatched extensibility and cross-platform compatibility. For those navigating the impact of generative AI on video editing, the need for seamless integration and user-centric design remains important.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you’re looking for a robust, modern video editing solution for your web application, explore our showcase and start a free trial.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Jan</dc:creator><media:content url="https://blog.img.ly/2024/12/Javascript-Editor--1-.jpg" medium="image"/><category>Video Editing</category><category>CE.SDK</category><category>Automation</category><category>Cross-Platform</category><category>JavaScript</category></item><item><title>CE.SDK Video Editor Now Available on Android</title><link>https://img.ly/blog/ce-sdk-video-editor-now-available-on-android/</link><guid isPermaLink="true">https://img.ly/blog/ce-sdk-video-editor-now-available-on-android/</guid><description>Integrate a fully-customizable Video Editor into your Android App with CE.SDK.</description><pubDate>Tue, 22 Oct 2024 11:44:02 GMT</pubDate><content:encoded>&lt;p&gt;We are excited to announce the release of CE.SDK’s video editing and video content creation tools for Android!&lt;/p&gt;
&lt;p&gt;After the successful iOS launch, CE.SDK’s professional video editing capabilities are now available for Android. Whether your app focuses on social media, e-commerce, or marketing technology, CE.SDK enables users to create high-quality videos directly within your app.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-36/uncompressed/Android-crop-split-trim.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h2 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;Getting up and running with an instance of the CE.SDK video editor for Android is easy.&lt;/p&gt;
&lt;p&gt;After downloading your license (get your trial license here) from your dashboard, simply &lt;a href=&quot;https://img.ly/docs/cesdk/android/prebuilt-solutions/video-editor-9e533a/&quot;&gt;invoke &lt;code&gt;VideoEditor&lt;/code&gt; as composable function, Activity or Fragment.&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;our-video-editing-backstory&quot;&gt;Our Video Editing Backstory&lt;/h2&gt;
&lt;p&gt;Since we launched VideoEditor SDK almost ten years ago, we have seen an astounding range of creative and unique use cases of our SDK in customer projects. From Cameo connecting celebrities and their fans in new ways, over Multibrain supercharging their users marketing campaigns with editable video content, to &lt;a href=&quot;https://zigazoo.com/&quot;&gt;Zigazoo&lt;/a&gt; powering video content within their social network for kids while putting safety and parental control first.&lt;/p&gt;
&lt;p&gt;Since then, millions of users have experienced IMG.LY’s VideoEditor SDK through our customers’ products, shaping how they expect to interact with and modify videos. Continuous feedback from both our customers and their users has also influenced the way we approach video interface design, helping us refine and innovate along the way.&lt;/p&gt;
&lt;p&gt;Our goal was to combine the reliability and robustness of the VideoEditor SDK with true cross-platform compatibility and the flexibility to meet diverse needs. By applying the lessons we’ve learned, we aimed to deliver an exceptional video editing user experience straight out of the box. We described how those learnings shaped the &lt;a href=&quot;https://img.ly/blog/designing-a-timeline-for-mobile-video-editing/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=androidvideo&quot;&gt;design of the mobile video editing interface.&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;key-capabilities&quot;&gt;Key Capabilities&lt;/h2&gt;
&lt;p&gt;With CE.SDK’s sleek timeline interface, your Android users can now take their video editing from simple transforms and adjustments to complex composition and multi-track editing:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Timeline-Based Editing&lt;/strong&gt;&lt;br&gt;
Split, trim, arrange video and audio tracks. Position elements such as images and stickers on the timeline and control their duration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multi-Track Editing&lt;/strong&gt;&lt;br&gt;
Work with multiple video and audio tracks, allowing for complex composition.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Transform &amp;#x26; Enhance&lt;/strong&gt;&lt;br&gt;
Crop, flip, rotate, and use presets for common video formats. Add filters, effects, blur and essential adjustments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Customizable Templates&lt;/strong&gt;&lt;br&gt;
Create and import templates using our web video editor, to jumpstart your users’ video composition and designs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Asset Integration&lt;/strong&gt;&lt;br&gt;
Integrate your own asset libraries for audio tracks, sound effects, images, and more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cross-platform Interoperability&lt;/strong&gt;&lt;br&gt;
CE.SDK’s video capabilities extends across iOS, Android and the Web, enabling cross-platform applications to offer seamless video editing capabilities. Users can edit and save work in progress and pick up on any device.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href=&quot;https://img.ly/showcases/cesdk/video-ui/android?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=androidvideo&quot;&gt;showcase app for an interactive demo&lt;/a&gt; of these features.&lt;/p&gt;
&lt;h2 id=&quot;customization&quot;&gt;Customization&lt;/h2&gt;
&lt;p&gt;The ability to tailor the video editor to your app’s particular use case has been a front and center concern in its development.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/docs/cesdk/android/configuration-2c1c3d/&quot;&gt;&lt;strong&gt;UI Customization&lt;/strong&gt;&lt;/a&gt;: While theming is currently limited to &lt;code&gt;dark&lt;/code&gt; and &lt;code&gt;light&lt;/code&gt; model, future releases will introduce more flexible theming, allowing you to modify the look and feel of the editor to match your app’s branding. Furthermore, we expose &lt;strong&gt;callbacks&lt;/strong&gt; that make it possible to hook into editor events and &lt;strong&gt;overlays&lt;/strong&gt; for custom dialogues.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/docs/cesdk/android/create-video-c41a08/&quot;&gt;&lt;strong&gt;Configure Video Presets&lt;/strong&gt;&lt;/a&gt;: You can configure the editor to create video presets based on your users’ needs. For instance, you may want to fix the video’s aspect ratio or optimize for certain resolutions, depending on the target platform.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/docs/cesdk/android/use-templates/apply-template-35c73e/&quot;&gt;&lt;strong&gt;Video Templates&lt;/strong&gt;&lt;/a&gt;: With the CE.SDK video editor web UI, you can create an unlimited number of video templates, such as collages, text designs, and animations, providing your users with professional-quality starting points for their video designs.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/docs/cesdk/android/import-media/asset-panel/customize-c9a4de/&quot;&gt;&lt;strong&gt;Assets&lt;/strong&gt;&lt;/a&gt;: Provide custom filters, fonts, and stickers to enhance the user experience. For niche apps, this can be an excellent way to engage users by offering content that feels unique to your community.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;ideal-for-social-media-ecommerce-and-marketing-tech-apps&quot;&gt;Ideal for Social Media, Ecommerce, and Marketing Tech Apps&lt;/h2&gt;
&lt;p&gt;CE.SDK’s new Android video editing capabilities are perfect for a variety of use cases, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/use-cases/story-reels-short-video-creation&quot;&gt;&lt;strong&gt;Social Media Applications&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;:&lt;/strong&gt; The CE.SDK video editor for Android excels at short-form video, ideal for social media applications, whether your users are publishing to other networks or you run a network yourself.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/industries/e-commerce&quot;&gt;&lt;strong&gt;Ecommerce&lt;/strong&gt;&lt;/a&gt;: Allow your users to create engaging product showcases, ads, and promotional videos, testimonials or reviews.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/industries/marketing-tech&quot;&gt;&lt;strong&gt;Marketing Tech Platforms&lt;/strong&gt;&lt;/a&gt;: Users can leverage our versatile templating to easily create polished social media and marketing graphics from within your app.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What’s Next?&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;Camera UI: Record videos on Android.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;988&quot; src=&quot;https://img.ly/_astro/camera-UI-Android-1_1FyMRS.webp&quot; srcset=&quot;/_astro/camera-UI-Android-1_ZjEi38.webp 640w, /_astro/camera-UI-Android-1_Z743FA.webp 750w, /_astro/camera-UI-Android-1_ZYwhh4.webp 828w, /_astro/camera-UI-Android-1_8a3zG.webp 1080w, /_astro/camera-UI-Android-1_19mjIs.webp 1280w, /_astro/camera-UI-Android-1_1FyMRS.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;This Android release is just the beginning! We are about to launch a &lt;strong&gt;Camera UI&lt;/strong&gt; for Android and are working on more advanced template creation features. So stay tuned.&lt;br&gt;
To see some of these features in action, check out our iOS demo, where the next phase of innovation is unfolding.&lt;/p&gt;
&lt;h2 id=&quot;get-started&quot;&gt;Get Started&lt;/h2&gt;
&lt;p&gt;Explore our &lt;a href=&quot;https://img.ly/showcases/cesdk/video-ui/android?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=androidvideo&quot;&gt;Video UI Showcase&lt;/a&gt; and &lt;a href=&quot;https://img.ly/docs/cesdk/android/prebuilt-solutions/video-editor-9e533a/&quot;&gt;Documentation for Android&lt;/a&gt; to discover how CE.SDK can streamline your content creation projects and make Android video editing easier than ever.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Over 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&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/10/android-video-editor-sdk-imgly-creative-editor_s.jpg" medium="image"/><category>Android</category><category>Android App Development</category><category>Creative Editor</category><category>Video Editing</category><category>Video Editor</category></item><item><title>A Modern Video Editor SDK for your Flutter App</title><link>https://img.ly/blog/a-modern-video-editor-sdk-for-your-flutter-app/</link><guid isPermaLink="true">https://img.ly/blog/a-modern-video-editor-sdk-for-your-flutter-app/</guid><description>Learn how to integrate IMG.LY&apos;s video editor for Flutter into your app and how to best leverage its capabilities.</description><pubDate>Fri, 13 Sep 2024 11:26:15 GMT</pubDate><content:encoded>&lt;p&gt;Video has been the only type of content with steadily growing demand, and it will remain a staple and an expected medium for users to create and consume.&lt;/p&gt;
&lt;p&gt;With platforms such as TikTok, Instagram Reels, and YouTube Shorts dominating and shaping user habits, the ability to create and edit videos directly within your app can significantly increase engagement.&lt;/p&gt;
&lt;p&gt;In this post, we’ll show how to integrate a video editor into a Flutter app using CreativeEditor SDK and customize it for specific use cases, like creating TikTok-style short videos.&lt;/p&gt;
&lt;p&gt;You can check out the code for the default and custom &lt;a href=&quot;https://github.com/imgly/cesdk-flutter-examples/tree/main/showcases/lib/data/code_examples/video&quot;&gt;Flutter video editor on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-add-a-video-editor-to-your-flutter-app&quot;&gt;Why add a Video Editor to Your Flutter App?&lt;/h3&gt;
&lt;p&gt;The last decade has made one thing clear: video content is king. Particularly, short-form dominates digital content. At the forefront of this trend are apps like TikTok, which made people more comfortable creating, editing, and sharing bite-sized clips.&lt;/p&gt;
&lt;p&gt;These apps have capitalized on their ability to let users apply filters, and add audio overlays, text, and more, empowering users at all skill levels to make professional-looking videos with minimal effort.&lt;/p&gt;
&lt;p&gt;A host of different use cases stand to profit from this trend by enhancing their user experience with video editing features, particularly marketing tech tooling and apps with a messaging or social media component.&lt;/p&gt;
&lt;p&gt;In general, video creation lowers the threshold for user-generated content and can positively impact distribution and product engagement.&lt;/p&gt;
&lt;p&gt;Flutter, Google’s UI toolkit for building apps across platforms from a single code base, is a perfect fit for &lt;a href=&quot;https://IMG.LY&quot;&gt;IMG.LY&lt;/a&gt;’s Video Editor. What often motivates the choice to use Flutter is that It ensures performance, flexibility, and a seamless user experience on iOS and Android devices. Similarly, our SDK is designed to offer a native cross-platform experience, while relying on the same underlying graphics processing engine, ensuring consistency and interoperability.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;getting-started-integrating-the-video-editor-in-flutter&quot;&gt;Getting Started: Integrating the Video Editor in Flutter&lt;/h3&gt;
&lt;p&gt;Let’s dive into how to set up a video editor in your Flutter app using CreativeEditor SDK.&lt;/p&gt;
&lt;h3 id=&quot;requirements&quot;&gt;Requirements&lt;/h3&gt;
&lt;p&gt;Before we get into the details, here’s what you’ll need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Flutter 3.16.0+&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dart 2.12.0+&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;iOS 16 or later&lt;/strong&gt; (as the video editor is currently only available for iOS, Android coming soon)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Swift 5.10 and Xcode 15.4&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;First, ensure your &lt;code&gt;pubspec.yml&lt;/code&gt; file includes the necessary 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;  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;  imgly_editor&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1.34.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once your environment is set up, you’re ready to start coding.&lt;/p&gt;
&lt;h3 id=&quot;setting-up-the-editor&quot;&gt;Setting Up the Editor&lt;/h3&gt;
&lt;p&gt;After adding the &lt;code&gt;imgly_editor&lt;/code&gt; dependency, import the package in your 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; &quot;package:imgly_editor/imgly_editor.dart&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next step is to initialize the editor by providing an instance of &lt;code&gt;EditorSettings&lt;/code&gt;. This setup requires a license key, which you can obtain from CreativeEditor SDK, and an optional &lt;code&gt;userId&lt;/code&gt; that helps track monthly active users (MAUs) across different devices.&lt;/p&gt;
&lt;p&gt;Here’s a basic example of how to open the video editor:&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;class&lt;/span&gt;&lt;span&gt; VideoEditorSolution&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  /// Opens the editor.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  void&lt;/span&gt;&lt;span&gt; openEditor&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; settings &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; EditorSettings&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;:&lt;/span&gt;&lt;span&gt; &quot;YOUR_LICENSE&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;YOUR_USER_ID&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; preset &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; EditorPreset&lt;/span&gt;&lt;span&gt;.video;&lt;/span&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 the editor and retrieve the editing result.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    final&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;.&lt;/span&gt;&lt;span&gt;openEditor&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        preset&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; preset,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        settings&lt;/span&gt;&lt;span&gt;:&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;span class=&quot;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 launches the editor with the video preset, enabling users to trim, cut, and add filters, text overlays, music, and more to their videos.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 814px) 814px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;814&quot; height=&quot;836&quot; src=&quot;https://img.ly/_astro/flutter-video-editor_Z1vXnTi.webp&quot; srcset=&quot;/_astro/flutter-video-editor_mCglm.webp 640w, /_astro/flutter-video-editor_bQizb.webp 750w, /_astro/flutter-video-editor_Z1vXnTi.webp 814w&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;use-cases-building-a-tiktok-like-experience&quot;&gt;Use Cases: Building a TikTok-Like Experience&lt;/h3&gt;
&lt;p&gt;Now that we have integrated the basic editor configuration into your Flutter app, let’s revisit some prominent 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 a short-form video, TikTok-like user experience, the most important factor is the ease of use of any video editor and content such as stickers or audio overlays.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The following SDK capabilities can help you achieve that goal:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Control Video Duration and Position in Time&lt;/strong&gt;: Users should be able to trim video clips on a timeline and position them relative to other tracks, such as audio clips.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Filters &amp;#x26; Effects&lt;/strong&gt;: Filters are essential for setting the tone of the video. Whether users want a retro, high contrast, or soft pastel look, filters provide creative control over the visual style.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Text &amp;#x26; Stickers&lt;/strong&gt;: Allow users to add text captions—especially important for content often watched on mute. Stickers and emojis can add a playful element to videos.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Music &amp;#x26; Audio&lt;/strong&gt;: Like TikTok, you can allow users to add background music or sound effects. Providing a library of popular tracks or sound snippets can enhance the content creation experience.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Templates&lt;/strong&gt;: Give your users a head start with customizable video templates for different themes, topics, and occasions. &lt;a href=&quot;https://IMG.LY&quot;&gt;IMG.LY&lt;/a&gt;’s CE.SDK comes with a web video editor to help build and deliver these templates to your Flutter app. 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&quot;&gt;video editor demo&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By integrating these features, you’ll empower users to create engaging, shareable content, elevating your app’s video creation experience to the level of consumer-grade social media apps.&lt;/p&gt;
&lt;h3 id=&quot;influencers-and-marketing&quot;&gt;Influencers and Marketing&lt;/h3&gt;
&lt;p&gt;If your app is targeting influencers or businesses, the ability to quickly create branded video content is key. You can offer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Branded Templates&lt;/strong&gt;: Pre-made templates that align with a brand’s style, allowing users to easily drop in their video footage and text while maintaining brand consistency. Again, check out the web editor demo to see how to build &lt;a href=&quot;https://img.ly/showcases/cesdk/video-ui/web?template=month-in-review&quot;&gt;video templates&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Watermarks&lt;/strong&gt;: Adding a watermark or logo as a brand signifier to videos can help with brand visibility and make sure content always points back to the creator.&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;ecommerce-and-user-generated-content&quot;&gt;eCommerce and User-Generated Content&lt;/h3&gt;
&lt;p&gt;Video is a powerful medium to showcase products, especially on marketplaces that can leverage user-generated content and enable vendors as well as customers to create product or review videos.&lt;/p&gt;
&lt;p&gt;For example, users could create unboxing videos, product reviews, or tutorials using the same trimming, filter, and music tools offered in a TikTok-style editor. These videos can then be shared on social media or used within the app to enhance the shopping experience.&lt;/p&gt;
&lt;p&gt;Likewise, showcasing products using authentic demo videos tap into familiar patterns of video consumption and can boost sales for vendors.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;customization-options-with-creativeeditor-sdk&quot;&gt;Customization Options with CreativeEditor SDK&lt;/h3&gt;
&lt;p&gt;We have designed the Flutter plugin to be adaptable to match your brand and use case-specific requirements. Soon the plugin will also support more advanced customization options such as the ability to &lt;strong&gt;activate or deactivate features by default.&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;UI Customization&lt;/strong&gt;: Modify the look and feel of the editor to match your app’s branding. We provide options for &lt;strong&gt;theming&lt;/strong&gt;, &lt;strong&gt;styling color palettes&lt;/strong&gt;, and &lt;strong&gt;callbacks&lt;/strong&gt; to hook into editor events.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Presets&lt;/strong&gt;: You can create custom video presets based on your users’ needs. For instance, you may want to limit video length or optimize for certain resolutions, depending on the target platform.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video Templates&lt;/strong&gt;: You can use the CE.SDK web UI to generate any number of video templates including collages, text designs, and animations to give your users professional-looking starting points for their video designs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Assets&lt;/strong&gt;: Provide custom filters, fonts, and stickers to enhance the user experience. For niche apps, this can be an excellent way to engage users by offering content that feels unique to your community.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Integrating a video editor into your Flutter app will 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. 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 outlined in this post, you can bring professional-level video editing features to your users. Explore CE.SDK’s video capabilities and &lt;a href=&quot;https://img.ly/docs/cesdk/flutter/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&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/09/how-to-flutter-photo-video-design-editor-sdk.png" medium="image"/><category>Video Editing</category><category>Flutter</category><category>Tutorial</category><category>Learning</category></item><item><title>Designing A Timeline For Mobile Video Editing</title><link>https://img.ly/blog/designing-a-timeline-for-mobile-video-editing/</link><guid isPermaLink="true">https://img.ly/blog/designing-a-timeline-for-mobile-video-editing/</guid><description>When we brought our desktop-class video editing capabilities to iOS, we learned some interesting lessons along the way. In this article, we’ll share the key user experience learnings from our design and development process.</description><pubDate>Tue, 20 Aug 2024 13:08:58 GMT</pubDate><content:encoded>&lt;p&gt;This article documents the most surprising insights from our journey building timeline user interface. We’ll explore everything from overcoming usability challenges, visualizing data, prioritizing information, and refining touch interactions.&lt;/p&gt;
&lt;p&gt;IMG.LY’s CE.SDK for iOS now comes with a &lt;a href=&quot;https://img.ly/showcases/cesdk/video-ui/ios&quot;&gt;native video editor&lt;/a&gt;, built from scratch in SwiftUI. I was responsible for coding and polishing the timeline user interface, working with both our design team and our cross-platform engine team. When I started implementing the timeline, we had the basic design concept sketched out in Figma. However, for an advanced UI like this, nothing beats rapid prototyping and iteration between design and development to make it &lt;em&gt;feel&lt;/em&gt; right.&lt;/p&gt;
&lt;h2 id=&quot;i-visualizing-time&quot;&gt;I. Visualizing Time&lt;/h2&gt;
&lt;p&gt;Timelines are a well-known convention, at least to modern humans. Timelines show durations in time as segments in space, distributed along the horizontal axis, with multiple tracks for things that happen in parallel. This is a simple yet effective way of representing moments in time visually. As obvious as it may seem today, this flavor of data visualization first appeared only 250 years ago. One of the earliest known examples is Joseph Priestley’s &lt;a href=&quot;https://en.wikipedia.org/wiki/A_Chart_of_Biography&quot;&gt;&lt;em&gt;A Chart of Biography&lt;/em&gt;&lt;/a&gt;, published in 1765. Here is Priestley’s &lt;em&gt;A New Chart of History&lt;/em&gt; from 1769, which is even more impressive:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Joseph Priestley, Public domain, via Wikimedia Commons&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;1399&quot; src=&quot;https://img.ly/_astro/A_New_Chart_of_History_s_Z11Q3oT.webp&quot; srcset=&quot;/_astro/A_New_Chart_of_History_s_Z1A1imS.webp 640w, /_astro/A_New_Chart_of_History_s_ZQ4nwl.webp 750w, /_astro/A_New_Chart_of_History_s_Z1RL1mX.webp 828w, /_astro/A_New_Chart_of_History_s_Zdfq9n.webp 1080w, /_astro/A_New_Chart_of_History_s_1uxfeA.webp 1280w, /_astro/A_New_Chart_of_History_s_1sCwTW.webp 1668w, /_astro/A_New_Chart_of_History_s_Z11Q3oT.webp 2000w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Video editing timelines are built on this exact principle of mapping time to space: A digital video composition consists of tracks for simultaneous audio and video layers. These tracks contain individual clips that can be rearranged, trimmed, and transformed. Navigating the video’s time dimension is as easy as scrolling left and right. The tracks and clips serve as landmarks for orientation, forming the mental model of what the video composition &lt;em&gt;is&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Timelines map time to space&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/embed/cKcksLW95-Y?feature=oembed&quot;&gt;https://www.youtube.com/embed/cKcksLW95-Y?feature=oembed&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;horizontal-direction&quot;&gt;Horizontal Direction&lt;/h3&gt;
&lt;p&gt;Modern iOS user interfaces appear flipped in right-to-left languages: the buttons and controls are in different horizontal positions. The layout follows the reading direction of the language. Even the familiar drill-down navigation animates to the left when drilling deeper into the hierarchy, and the &lt;em&gt;Back&lt;/em&gt; button sits in the upper right corner, its chevron pointing to the right. So, naturally, one of the first things we checked was whether timelines should run right-to-left in writing systems like Arabic or Hebrew. According to a trustworthy source, this is not the case: we found an old version of Apple’s developer guides stating that &lt;a href=&quot;https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPInternational/SupportingRight-To-LeftLanguages/SupportingRight-To-LeftLanguages.html&quot;&gt;editor timelines in software are universally left-to-right&lt;/a&gt;, even if the surrounding interface layout is flipped by the operating system for right-to-left system languages.&lt;/p&gt;
&lt;h3 id=&quot;cognitive-load-vs-discoverability&quot;&gt;Cognitive Load vs. Discoverability&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;Variations of clip heights within our video timeline&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1280px) 1280px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1280&quot; height=&quot;1280&quot; src=&quot;https://img.ly/_astro/timeline_heights-s-1_Zf1XyL.webp&quot; srcset=&quot;/_astro/timeline_heights-s-1_loE2x.webp 640w, /_astro/timeline_heights-s-1_1N9hmK.webp 750w, /_astro/timeline_heights-s-1_ZPCwAb.webp 828w, /_astro/timeline_heights-s-1_PsMHu.webp 1080w, /_astro/timeline_heights-s-1_Zf1XyL.webp 1280w&quot;&gt;&lt;/p&gt;
&lt;p&gt;When designing our video timeline, we experimented with both the size of the clips and with the dimensions of the timeline itself. How much can you fit on a screen before it looks cluttered? How large can the controls be without being clumsy and inefficient? The only way to find out is to try it out right on the device.&lt;/p&gt;
&lt;p&gt;Hiding features in dropdown menus is easy. Banishing them to separate screens is even easier. But hiding functionality hurts discoverability. Out of sight, out of mind. We think user interfaces in productivity apps should be the opposite. They should prioritize &lt;a href=&quot;https://blog.codinghorror.com/information-density-and-dr-bronner/&quot;&gt;high information density&lt;/a&gt; and avoid extra navigation steps as much as possible.&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;538&quot; src=&quot;https://img.ly/_astro/timeline-density_Z1v9BvG.webp&quot; srcset=&quot;/_astro/timeline-density_Ze8fWQ.webp 640w, /_astro/timeline-density_1xRR1T.webp 750w, /_astro/timeline-density_Z1EYVqv.webp 828w, /_astro/timeline-density_UKvMA.webp 1080w, /_astro/timeline-density_Z1v9BvG.webp 1200w&quot;&gt;&lt;/p&gt;
&lt;p&gt;We increased the density of the toolbar that sits below the timeline: Our goal was to make all the important features easily accessible, despite the small screen. We achieved this by changing the single floating &lt;em&gt;Add&lt;/em&gt; button from the initial designs to a more granular set of buttons to add specific asset types, like stickers and text. This not only increases efficiency for frequent users, it also makes the capabilities of the video editor more obvious and approachable.&lt;/p&gt;
&lt;h3 id=&quot;adaptive-height&quot;&gt;Adaptive Height&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;Left: Small timeline in an empty scene. Right: Large timeline in a complex scene.&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;2089&quot; src=&quot;https://img.ly/_astro/scenes-empty-full-s_Zq9sal.webp&quot; srcset=&quot;/_astro/scenes-empty-full-s_Z1wkWJX.webp 640w, /_astro/scenes-empty-full-s_26v1Ib.webp 750w, /_astro/scenes-empty-full-s_ZosLXG.webp 828w, /_astro/scenes-empty-full-s_Z2nAm3m.webp 1080w, /_astro/scenes-empty-full-s_V3mUA.webp 1280w, /_astro/scenes-empty-full-s_Z1blOTE.webp 1668w, /_astro/scenes-empty-full-s_Zq9sal.webp 2000w&quot;&gt;&lt;/p&gt;
&lt;p&gt;As space is at a premium on a small screen, we wanted to make the video preview as large as possible. We decided to allow the user to collapse the timeline at any time to focus on the video canvas. And even without manually minimizing the timeline, it doesn’t waste space: the timeline adjusts its height to fit snugly around its contents. It only grows as you add more clips.&lt;/p&gt;
&lt;h3 id=&quot;units-and-scale&quot;&gt;Units and Scale&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/building-a-timeline/zoom-levels.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;The numeric scale is an essential part of any timeline. It helps you see the &lt;em&gt;in&lt;/em&gt; and &lt;em&gt;out&lt;/em&gt; time of a clip at a glance, and the permanently visible ruler makes changes in the zoom level easy to understand. We added dots as subdivisions between the ruler’s numeric labels, which gives it a nice tool-like character. At full zoom—when the ruler shows 5-second intervals—we decided to use four dots in between, so there is one dot for each second. We think this detail adds a welcome sense of precision.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/building-a-timeline/clip-label.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;The clips themselves have a duration label when they are selected. If the duration is below 10, you get a fractional digit (e.g., 4.2s). For the ruler, we went with the same short format (5s, 10s, 30s, etc.) for all values below one minute, and the technical format (1:00, 1:30, etc.) for the rest. Of course, we use iOS’s built-in &lt;a href=&quot;https://swiftbysundell.com/articles/formatting-numbers-in-swift/&quot;&gt;number formatters&lt;/a&gt; to get properly formatted and localized strings for our labels.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/building-a-timeline/ruler-rounding.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;An important detail for the label that shows the current playback timecode: The seconds must always be rounded down. If the playhead sits between two seconds, the lower number must be displayed until the playhead crosses the indicator for the higher second. Simple rounding to the nearest integer would be confusing, because it doesn’t match our expectations when dealing with timelines. Five seconds do not count as five seconds until they have elapsed in their entirety.&lt;/p&gt;
&lt;h2 id=&quot;ii-manipulating-time&quot;&gt;II. Manipulating Time&lt;/h2&gt;
&lt;p&gt;What &lt;em&gt;looks&lt;/em&gt; good is not guaranteed to &lt;em&gt;feel&lt;/em&gt; good when you interact with it. A video editor timeline is more than just a visualization; it’s also a user interface. As such, it should be self-explanatory and provide constant feedback as the user manipulates it.&lt;/p&gt;
&lt;h3 id=&quot;fingertip-sized&quot;&gt;Fingertip-sized&lt;/h3&gt;
&lt;p&gt;The clips must be large enough to be easy to touch and at the same time detailed enough to give a decent overview. The size that can be easily tapped by most people &lt;a href=&quot;https://www.nngroup.com/articles/touch-target-size/&quot;&gt;is around one square centimeter&lt;/a&gt;, which matches the 44 × 44 pt default size of iPhone system controls. Our testing showed that we could go a little smaller. Editing video is a focused task, so the tap targets don’t need to be as robust as for tasks that happen while you, say, run for the bus. And all user interfaces should be scalable for accessibility anyhow, so we didn’t hard-code any sizes and we also made the dimensions adjustable. And, speaking of adapting to individual user needs: of course, all the text labels in our timeline are &lt;a href=&quot;https://developer.apple.com/documentation/uikit/scaling-fonts-automatically&quot;&gt;automatically scaled&lt;/a&gt; depending on your device settings.&lt;/p&gt;
&lt;h3 id=&quot;handles&quot;&gt;Handles&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;Hit area of timeline handles&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1179px) 1179px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1179&quot; height=&quot;438&quot; src=&quot;https://img.ly/_astro/hit-area_Z1THQer.webp&quot; srcset=&quot;/_astro/hit-area_Z1Qmzes.webp 640w, /_astro/hit-area_JoIVh.webp 750w, /_astro/hit-area_Z1I3V8z.webp 828w, /_astro/hit-area_Z1VwXEK.webp 1080w, /_astro/hit-area_Z1THQer.webp 1179w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Selected clips in the timeline get a bold blue frame with large handles on either side. These handles are used to trim the clip’s length. For comfortable tapping and dragging, the handles must be larger than their visual size. In fact, we made the hit area more than twice as wide as the visual appearance suggests.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Triangular indents on trim handles&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 500px) 500px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;500&quot; height=&quot;500&quot; src=&quot;https://img.ly/_astro/triangle_Z1UocRe.webp&quot; srcset=&quot;/_astro/triangle_Z1UocRe.webp 500w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Early on in our design process, we realized that the trim handles had to sit on the outside of the clip, extending beyond the left and right edge. This made it hard to see the clip’s actual size, so we added little grooves: little triangular indents that point to the start and end of the selected clip.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/building-a-timeline/clip-trim-bounce.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Some clips in the timeline can be extended to any duration, like text or stickers. Other clips have a finite footage duration, like video and audio clips. To make this difference visible, clips with a finite footage duration reveal a ghost outline in the background while they are resized, featuring &lt;a href=&quot;https://en.wikipedia.org/wiki/Marching_ants&quot;&gt;marching ants&lt;/a&gt;. The handle icon changes from an outward-pointing chevron to a vertical line when the clip can’t be made longer.&lt;/p&gt;
&lt;h3 id=&quot;scrolling-and-zooming&quot;&gt;Scrolling and Zooming&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/building-a-timeline/scroll-zoom.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;A rather invisible detail was one of the trickiest things to get right: the coordination of all the different finger gestures on the timeline.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Horizontal Scrolling:&lt;/strong&gt; Timeline scrolls horizontally to move in time.&lt;br&gt;
&lt;strong&gt;Vertical Scrolling:&lt;/strong&gt; Timeline scrolls vertically providing room for tracks.&lt;br&gt;
&lt;strong&gt;Clip Movement:&lt;/strong&gt; Dragging horizontally on a selected clip moves it.&lt;br&gt;
&lt;strong&gt;Duration Adjustment:&lt;/strong&gt; Dragging the trim handles adjusts the clip’s duration.&lt;br&gt;
&lt;strong&gt;Pinch-to-Zoom:&lt;/strong&gt; Pinching with two fingers changes the zoom level of the visualization.&lt;/p&gt;
&lt;p&gt;All these gestures must behave as expected and not interfere with each other. We found it unexpectedly difficult to fine-tune and harmonize these interactions with pure SwiftUI, so we used some proven legacy iOS techniques to get it right. If you’re interested in the technical details of our solution with &lt;code&gt;SwiftUIIntrospect&lt;/code&gt; and &lt;code&gt;UIGestureRecognizers&lt;/code&gt;, check the &lt;a href=&quot;https://github.com/imgly/IMGLYUI-swift/tree/main/Sources/IMGLYEditor/Timeline/TimelineView&quot;&gt;source code&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;snapping&quot;&gt;Snapping&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/building-a-timeline/snapping.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Video editing must feel precise, even on a small screen. When you trim or move clips, they should snap to other clips, to the playhead, and to the start and the end of the timeline. To show which position the selected clip snaps to, we designed animated dotted lines. We use the iPhone’s haptic engine to provide subtle feedback for snapping, which really helps to make the interaction feel natural—like a notch snapping into a detent. Something that we didn’t get right at first: Snapping should only respect those snapping points that are currently visible in the viewport. Of course! Otherwise, a partially visible clip may snap to something offscreen, which is very confusing.&lt;/p&gt;
&lt;h3 id=&quot;scrubbing-preview&quot;&gt;Scrubbing Preview&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/building-a-timeline/scrubbing.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;For editing a video clip to feel precise, you need to be able to see exactly &lt;em&gt;where&lt;/em&gt; you’re trimming. But the playback position in our mobile timeline can’t change while you’re trimming, because that would mean scrolling the timeline—which would move the very clip you’re trimming. The trim preview needs to be independent of the composition’s playback position. We solved this by adding a temporary overlay with its own playback time to the canvas. When you start dragging one of the handles on either side of the clip, the overlay shows the exact trim position of the video clip. When you release, the preview returns to the playback position.&lt;/p&gt;
&lt;h2 id=&quot;try-it-out&quot;&gt;Try it out!&lt;/h2&gt;
&lt;p&gt;“The only way to experience an experience is to experience it,” said interaction designer Bill Moggridge, and we couldn’t agree more! If you’re curious how our new video timeline &lt;em&gt;feels&lt;/em&gt;, &lt;a href=&quot;https://apps.apple.com/en/app/img-ly-design-editor/id1672991141&quot;&gt;download our iOS app&lt;/a&gt; and try editing your own videos in our brand-new timeline experience.&lt;/p&gt;
&lt;p&gt;If you’re a developer, check out &lt;a href=&quot;https://img.ly/docs/cesdk/ios/starterkits/video-editor-e1nlor/&quot;&gt;the video editor documentation&lt;/a&gt;. With IMG.LY’s SDK, you can add all of these video editing capabilities to your own apps in a breeze.&lt;/p&gt;</content:encoded><dc:creator>Frank</dc:creator><media:content url="https://blog.img.ly/2024/08/build-a-timeline-video-app-s.jpg" medium="image"/><category>App Development</category><category>UI/UX</category><category>Video Editing</category><category>Insights</category></item><item><title>CE.SDK v1.25 Release Notes</title><link>https://img.ly/blog/creative-editor-sdk-v-1-25-0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v-1-25-0-release-notes/</guid><description>Recolor images, such as T-shirt mockups, remove green screen backgrounds, and preview audio directly in your app. </description><pubDate>Tue, 07 May 2024 11:43:59 GMT</pubDate><content:encoded>&lt;p&gt;Welcome to the latest update from our team! Since our &lt;a href=&quot;https://img.ly/blog/creative-editor-sdk-v-1-23-0-release-notes/&quot;&gt;last release&lt;/a&gt;, we have been working to bring more creative capabilities to your app and improve user experience. With this release, you can:&lt;/p&gt;
&lt;h2 id=&quot;recolor-images-like-t-shirt-mockups&quot;&gt;Recolor Images like T-shirt Mockups&lt;/h2&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-25/recolor-images-editor-sdk.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Replace colors in any image with our new recoloring feature. Select a color you want to change, choose its replacement, and apply the changes. Recoloring is ideal for tasks like altering the color of specific elements, such as t-shirts in product photos.&lt;/p&gt;
&lt;p&gt;Simply choose your image, navigate to Effects, and select “Recolor”. Choose the color you want to change (Source Color), and the color you want to replace it with (Target Color). For a more refined result, adjust “Smoothness”, “Color Match”, and “Brightness Match”.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-25/recoloring_androids.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;This feature is available on &lt;strong&gt;Web&lt;/strong&gt;, &lt;strong&gt;Android&lt;/strong&gt;, and &lt;strong&gt;iOS&lt;/strong&gt; platforms.&lt;/p&gt;
&lt;p&gt;Check our &lt;a href=&quot;https://img.ly/docs/cesdk/js/filters-and-effects/add-c796ba/&quot;&gt;documentation&lt;/a&gt; on filters and effects to learn more.&lt;/p&gt;
&lt;h2 id=&quot;create-transparency-with-a-green-screen&quot;&gt;Create Transparency with a Green Screen&lt;/h2&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-25/green-screen-video-sdk.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Easily make green screen backgrounds transparent in your &lt;strong&gt;images&lt;/strong&gt; and &lt;strong&gt;videos on the Web,&lt;/strong&gt; and for images on &lt;strong&gt;Android and iOS&lt;/strong&gt;. Our Green Screen Effect is tailored to identify and eliminate green backdrops—or any other color you specify. Perfect for content creators and videographers who need to isolate subjects without manually editing backgrounds.&lt;/p&gt;
&lt;p&gt;Select an image or video clip with a color background you wish to remove. Then choose “Green Screen” in the Effects, and our effect handles the rest. Fine-tune the effect to your liking.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-25/greenscreen_android.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h2 id=&quot;user-interface-improvements&quot;&gt;User Interface Improvements&lt;/h2&gt;
&lt;p&gt;In our latest update, we’ve focused on enhancing the usability and functionality of our interface. Here’s what’s new:&lt;/p&gt;
&lt;h3 id=&quot;audio-preview&quot;&gt;Audio Preview&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-25/audio-preview-web-video-sdk.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Choosing the right audio clip is now easier with our new Audio Preview feature. Preview audio clips directly within the interface and make the perfect selection for your projects.&lt;/p&gt;
&lt;h3 id=&quot;selection-frame-improvements&quot;&gt;Selection Frame Improvements&lt;/h3&gt;
&lt;p&gt;We’ve upgraded our selection frames with new icons, hover effects, enhanced drag functionality, and shadow rendering. These improvements make manipulating elements within our platform more intuitive and visually appealing. Whether you’re rearranging components or resizing elements, our refined handling ensures a smoother interaction.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Please note that these release notes also include improvements from v1.24.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading. Join over 3000 specialists with powerful apps, and&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;&lt;strong&gt;subscribe&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;to our newsletter. We keep you in the loop with brand-new features, early access, and updates.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2024/05/creative-sdk-1-25.jpg" medium="image"/><category>Release Notes</category><category>CE.SDK</category><category>Video Editing</category><category>Design Editor</category><category>App Development</category></item><item><title>CE.SDK v1.23 Release Notes</title><link>https://img.ly/blog/creative-editor-sdk-v-1-23-0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v-1-23-0-release-notes/</guid><description>Record videos on iOS with a dual camera, and edit on a timeline. Add editors to mobile apps quickly with UI Packages, and improved Video Editor for Web.</description><pubDate>Thu, 28 Mar 2024 09:47:34 GMT</pubDate><content:encoded>&lt;p&gt;Since our &lt;a href=&quot;https://img.ly/blog/creative-editor-sdk-v-1-21-0-release-notes/&quot;&gt;last release&lt;/a&gt;, we’ve been crafting new features to empower your users’ creative journey. In the past month, we have received valuable insight from you in our exclusive Beta testing. Now we are excited to announce CE.SDK v1.23!&lt;/p&gt;
&lt;h2 id=&quot;record-and-edit-short-form-video-content-on-ios&quot;&gt;Record and Edit Short-Form Video Content on iOS&lt;/h2&gt;
&lt;p&gt;Video Content Creation is here! Your users can record videos, blend them with audio, text, and graphics on a timeline. This includes features like countdown timer, and the popular split screen modes for reactions and duets. You may recognize these from Instagram &lt;strong&gt;Reels&lt;/strong&gt; or &lt;strong&gt;TikTok&lt;/strong&gt;. Making adjustments is straightforward using filters, effects, and adjustment settings.&lt;/p&gt;
&lt;h3 id=&quot;record-with-a-new-camera&quot;&gt;Record with a New Camera&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-23/Camera-UI-camera-sdk-ios.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Record a video and utilize the &lt;strong&gt;Dual Camera&lt;/strong&gt; feature. This allows you to record simultaneously with your front and back camera, producing a split-screen video. Determine when the recording begins using a countdown &lt;strong&gt;timer&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;edit-on-a-timeline&quot;&gt;Edit on a Timeline&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-23/timeline-video-editor-sdk-ios.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Users can reorder, arrange and split clips on a timeline, and use filters, visual effects, along with other adjustments to modify the &lt;strong&gt;appearance&lt;/strong&gt; of clips and images. Add &lt;strong&gt;audio&lt;/strong&gt; tracks, image or video &lt;strong&gt;overlays&lt;/strong&gt;, format text, integrate stickers, and use tools for cropping, filtering, blurring, and adjusting images and videos. Try recording and editing Video Content with our &lt;a href=&quot;https://apps.apple.com/de/app/img-ly-design-editor/id1672991141&quot;&gt;Demo App&lt;/a&gt; for iOS.&lt;/p&gt;
&lt;p&gt;Get started with our &lt;a href=&quot;https://img.ly/docs/cesdk/ios/prebuilt-solutions/video-editor-9e533a/&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;quickly-add-editors-to-mobile-apps-with-ui-packages&quot;&gt;Quickly Add Editors to Mobile Apps with UI Packages&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/1-12_packages_Z1xR7ci.webp&quot; srcset=&quot;/_astro/1-12_packages_2d48mc.webp 640w, /_astro/1-12_packages_ZyzF3C.webp 750w, /_astro/1-12_packages_Z1jY4Ix.webp 828w, /_astro/1-12_packages_Z1esFNR.webp 1080w, /_astro/1-12_packages_Z1oaov5.webp 1280w, /_astro/1-12_packages_Z1xR7ci.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the past, integrating our editors into your app required developers to manually collect code from our showcases and piece together the UI. This not only demanded effort but also necessitated frequent updates to ensure compatibility with each of our new releases.&lt;/p&gt;
&lt;p&gt;To free your time and resources, we are now introducing ready-to-use &lt;strong&gt;UI packages&lt;/strong&gt; for &lt;strong&gt;iOS&lt;/strong&gt; and &lt;strong&gt;Android&lt;/strong&gt;, that bring advanced editors directly to your app. This eliminates the previous labor-intensive process. These packages include a wide range of options, from a fully equipped Video Editor (iOS) and Camera UI (iOS) to specialized examples like an Apparel Editor or Postcard Editor.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Get started instantly, by installing a UI package. From left to right: Camera UI, Video Editor UI - Timeline View, Video Editor UI, Example UIs for Apparel and Postcard Editors.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/1-23-UI-Packages-SDK_Ba7YI.webp&quot; srcset=&quot;/_astro/1-23-UI-Packages-SDK_14KBdi.webp 640w, /_astro/1-23-UI-Packages-SDK_Z1CUB1w.webp 750w, /_astro/1-23-UI-Packages-SDK_2jitUd.webp 828w, /_astro/1-23-UI-Packages-SDK_nrVDy.webp 1080w, /_astro/1-23-UI-Packages-SDK_Z22M25k.webp 1280w, /_astro/1-23-UI-Packages-SDK_Ba7YI.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;We maintain UI packages for you, ensuring compatibility across devices and development environments. Now, you can immediately start with fully featured editors, staying up-to-date with just a click, and without manual intervention. Get started with our &lt;a href=&quot;https://img.ly/docs/cesdk/ios/get-started/overview-e18f40/&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;integrate-a-user-friendly-editor&quot;&gt;Integrate a User-Friendly Editor&lt;/h2&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-23/design-editor-creative-sdk.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;We’ve made enhancements for a more user-friendly editor setup of CE.SDK.&lt;/p&gt;
&lt;p&gt;To enhance &lt;strong&gt;legibility&lt;/strong&gt; and perception of our UI elements, we have reworked our &lt;strong&gt;UI colors&lt;/strong&gt;, making sure they meet the minimal contrast requirements for an improved experience. Additionally, the active state of settings is now more apparent than it was previously.&lt;/p&gt;
&lt;p&gt;Next, libraries and all inspectors open on the same side of our default editor now, instead of on both sides. This reduces mouse travel, saves space, and &lt;strong&gt;streamlines workflows&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Additionally, you can now &lt;strong&gt;resize&lt;/strong&gt; images more easily by dragging from any side, not just using the handles. This same feature is also applicable when cropping your image.&lt;/p&gt;
&lt;p&gt;Lastly, elements &lt;strong&gt;stay selected&lt;/strong&gt; after grouping or ungrouping. In the past, changing group settings would deselect your elements.&lt;/p&gt;
&lt;h2 id=&quot;integrate-a-smooth-video-editor-into-your-app&quot;&gt;Integrate A Smooth Video Editor into your App&lt;/h2&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-23/video-editor-web-sdk.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;We recently launched our Video Editor for the Web. Since the release, we significantly &lt;strong&gt;improved the playback performance&lt;/strong&gt;, leading to smoother editing and playback.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;timeline&lt;/strong&gt; lets you arrange and edit videos, photos, text, audio, shapes, and stickers. Enhance projects with filters or adjustments, and add a personal touch with customizable audio. Precisely split, trim, and replace elements. Add audio files to make your project stand out. Try editing yourself with our &lt;a href=&quot;https://img.ly/showcases/cesdk/video-ui/web?template=sales/?utm_source=blog&amp;#x26;utm_medium=organic&amp;#x26;utm_campaign=video-for-web-sales&quot;&gt;Showcase for Sales Outreach Videos&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Please note that these release notes also include improvements from v1.22.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading. Join over 3000 specialists with powerful apps, and&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;&lt;strong&gt;subscribe&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;to our newsletter. We keep you in the loop with brand-new features, early access, and updates.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2024/03/1-23-video-editing-ios-tiktok-app-sdk.jpg" medium="image"/><category>Release Notes</category><category>CE.SDK</category><category>Video Editing</category><category>iOS App Development</category></item><item><title>CE.SDK v1.21 Release Notes</title><link>https://img.ly/blog/creative-editor-sdk-v-1-21-0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v-1-21-0-release-notes/</guid><description>Create stunning videos using a brand-new timeline for web.</description><pubDate>Thu, 22 Feb 2024 16:22:28 GMT</pubDate><content:encoded>&lt;p&gt;Since our &lt;a href=&quot;https://img.ly/blog/creative-editor-sdk-v_1_20_0-release-notes/&quot;&gt;last release&lt;/a&gt;, we’ve been crafting new features to empower your users’ creative journey. Today, we are happy to introduce CE.SDK v1.21!&lt;/p&gt;
&lt;h3 id=&quot;create-videos-with-a-new-timeline-for-web&quot;&gt;Create Videos With A New Timeline for Web&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-21/video-for-web-121-s.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Create eye-catching videos within your web app for any platform, including Instagram, TikTok, or YouTube! Here’s what’s new: the integrated &lt;strong&gt;timeline&lt;/strong&gt; supports the arrangement of elements like videos, photos, text, audio, shapes and stickers. Users can fine-tune their projects with adjustments or filters, and personalize their creations with audio from a customizable library.&lt;/p&gt;
&lt;p&gt;To simplify your content creation, use our formats that automatically &lt;strong&gt;set your canvas size&lt;/strong&gt; &lt;strong&gt;for social media&lt;/strong&gt; and other popular video resolutions.&lt;/p&gt;
&lt;p&gt;Effectively &lt;strong&gt;arrange&lt;/strong&gt; and edit multiple elements within your videos, offering precise control over the editing process. Easily split, trim, and replace. Lastly, make it pop by adding audio files to the composition.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Refine designs in our Showcase by customizing templates: adjust filters, swap visuals or audio, including integration with Soundstripe for premium stock audio.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1964px) 1964px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1964&quot; height=&quot;1468&quot; src=&quot;https://img.ly/_astro/grafik-1_1f8RgA.webp&quot; srcset=&quot;/_astro/grafik-1_Z270Y1B.webp 640w, /_astro/grafik-1_mTAMV.webp 750w, /_astro/grafik-1_1wMhS0.webp 828w, /_astro/grafik-1_Z1e88GP.webp 1080w, /_astro/grafik-1_ZhzA1h.webp 1280w, /_astro/grafik-1_27z0Wj.webp 1668w, /_astro/grafik-1_1f8RgA.webp 1964w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Creating &lt;strong&gt;templates&lt;/strong&gt; with placeholders for your users is just as easy. Simply select an element, click ‘Placeholder’, and decide which elements can be edited.&lt;/p&gt;
&lt;p&gt;To integrate, browse our &lt;a href=&quot;https://img.ly/docs/cesdk/js/prebuilt-solutions/video-editor-9e533a/&quot;&gt;Guide&lt;/a&gt;. Try the &lt;a href=&quot;https://img.ly/showcases/cesdk/video-ui/web&quot;&gt;video editor live demo&lt;/a&gt; yourself. This showcase contains templates, audio and video examples for you to try.&lt;/p&gt;
&lt;h3 id=&quot;keep-designs-in-focus-on-ios&quot;&gt;Keep Designs in Focus on iOS&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-21/iOS-zoom-apparel-designer-sdk.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Our new Camera Zoom Limits make editing a design easier. Your page stays in focus to &lt;strong&gt;avoid accidental scrolling&lt;/strong&gt; outside the design. The zoom level is set within a minimum and maximum range, ensuring a practical, usable view at all times. When you select an element in your design, it stays in focus when opening adjustments or other inspectors. It allows you to immediately see changes made to the selected element. This improvement is available for iOS and Android, and can be tested in our Design Editor Demo on the &lt;a href=&quot;https://apps.apple.com/de/app/img-ly-design-editor/id1672991141&quot;&gt;App Store&lt;/a&gt; and &lt;a href=&quot;https://play.google.com/store/apps/details?id=ly.img.cesdk.catalog&quot;&gt;Google Play&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;ensure-color-purity-in-print&quot;&gt;Ensure Color Purity in Print&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/1-12_underlayer_Z1gSAz3.webp&quot; srcset=&quot;/_astro/1-12_underlayer_1vaO7T.webp 640w, /_astro/1-12_underlayer_ZiCV0p.webp 750w, /_astro/1-12_underlayer_1QlNIp.webp 828w, /_astro/1-12_underlayer_1J6A8t.webp 1080w, /_astro/1-12_underlayer_Z2iYz7K.webp 1280w, /_astro/1-12_underlayer_Z1gSAz3.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Our newly introduced &lt;strong&gt;Underlayer API&lt;/strong&gt; guarantees the preservation of color purity whenever you export designs using our CE.SDK for print. This solution specifically addresses the challenge of maintaining the richness and vibrancy of colors, irrespective of the color of the canvas, which could range from apparel to paper.&lt;/p&gt;
&lt;p&gt;When you’re ready to export your design, this feature adds a special ‘&lt;strong&gt;underbase&lt;/strong&gt;’ layer, underneath your design. This layer, typically a white or specific primer coat, makes sure the final printed colors match your original vision perfectly.&lt;/p&gt;
&lt;h3 id=&quot;very-soon-integrate-short-form-video-creation&quot;&gt;Very soon: Integrate Short-Form Video Creation&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1920px) 1920px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1920&quot; height=&quot;1080&quot; src=&quot;https://img.ly/_astro/VCC_head-loading_s_ZTQpN2.webp&quot; srcset=&quot;/_astro/VCC_head-loading_s_Z1FDdaR.webp 640w, /_astro/VCC_head-loading_s_17HiQ3.webp 750w, /_astro/VCC_head-loading_s_5DATp.webp 828w, /_astro/VCC_head-loading_s_69D9S.webp 1080w, /_astro/VCC_head-loading_s_Z1ghRHI.webp 1280w, /_astro/VCC_head-loading_s_ZaUdJP.webp 1668w, /_astro/VCC_head-loading_s_ZTQpN2.webp 1920w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Video remains key for attracting, engaging and retaining audiences, especially on mobile. Creating a video editor can be tough, thus we’re thrilled to soon release our turnkey solution for &lt;strong&gt;mobile video content creation&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Combined with our new &lt;a href=&quot;https://img.ly/products/camera-sdk&quot;&gt;Camera SDK&lt;/a&gt;, users can seamlessly blend video, audio, text, and graphics on a sleek timeline. It includes features like voiceover, zoom, tap to record, and the popular split screen modes for reactions and duets. You may recognize these from Instagram or TikTok!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Last chance:&lt;/strong&gt; &lt;strong&gt;Secure your waitlist spot&lt;/strong&gt; to our Video Content Creation Release. You will also receive access to our TestFlight app.&lt;br&gt;
Registration is only open until March 6, 2024.&lt;/p&gt;
&lt;p&gt;? &lt;a href=&quot;https://share.hsforms.com/1mrIXiBbURn6sMqYgZG9c6A1hk3i&quot;&gt;Gain Access&lt;/a&gt; to our Video Content Creation Release&lt;br&gt;
? &lt;a href=&quot;https://2498814.fs1.hubspotusercontent-na1.net/hubfs/2498814/Newsletter-Digest/nov-2023/IMGLY_VIDEO_SDK.pdf?utm_campaign=Video%20Content%20Creation&amp;#x26;utm_medium=releasenotes&amp;#x26;_hsmi=2&amp;#x26;utm_content=2&amp;#x26;utm_source=blog&quot;&gt;Download Feature Exposé&lt;/a&gt; instantly (PDF)&lt;/p&gt;
&lt;p&gt;We can’t wait to hear your feedback!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading. Join over 3000 specialists with powerful apps, and&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;&lt;strong&gt;subscribe&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;to our newsletter. We keep you in the loop with brand-new features and updates.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2024/02/video-web-editor-sdk-imgly-s.jpg" medium="image"/><category>Release Notes</category><category>Video Editing</category><category>Video Editor</category><category>Web-to-print</category><category>Print</category><category>iOS App Development</category></item><item><title>CE.SDK v1.19 Release Notes</title><link>https://img.ly/blog/creative-editor-sdk-v1-19-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v1-19-release-notes/</guid><description>Explore unified editing of your graphic blocks, new showcases, and an exciting announcement for video!</description><pubDate>Fri, 22 Dec 2023 14:26:48 GMT</pubDate><content:encoded>&lt;p&gt;Since our last release, we’ve been crafting new features to empower your users’ creative journey. Today, we’re thrilled to introduce CE.SDK v1.19! With this release, you can:&lt;/p&gt;
&lt;h3 id=&quot;unify-design-power-with-block-unification&quot;&gt;Unify Design Power with Block Unification&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-19/block-unification_1-19-videofills.mp4&quot; controls muted playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Introducing Block Unification for CE.SDK on Mobile and Web—a feature that simplifies design versatility. Videos, images, shapes, and stickers now seamlessly come together as one Graphic Block, easily modified within a unified inspector. All supported settings for your selected block, including strokes, filters, and adjustments, are conveniently displayed in a single, user-friendly space.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Unified Graphic Block&lt;/strong&gt;&lt;br&gt;
Experience a singular Graphic Block supporting diverse fills (images, videos, colors, gradients) and shapes. Switch fill types (video, image, color) effortlessly, enabling dynamic designs in one place.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Easy Fine-Tuning&lt;/strong&gt;&lt;br&gt;
Block Unification integrates effects, filters, adjustments, and blur uniformly across all design elements. Elevate your designs with precision and consistency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A Treat for Users and Developers&lt;/strong&gt;&lt;br&gt;
For developers, Block Unification means streamlined code maintenance. The consolidation of design blocks into one Graphic Block simplifies development, allowing more focus on crafting exceptional design experiences with IMG.LY.&lt;/p&gt;
&lt;h3 id=&quot;enhance-interaction-with-mouse-wheel-support&quot;&gt;Enhance Interaction with Mouse Wheel Support&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;mousewheel.jpg&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/mousewheel_ZdLdi7.webp&quot; srcset=&quot;/_astro/mousewheel_2evOYG.webp 640w, /_astro/mousewheel_1btqKu.webp 750w, /_astro/mousewheel_Z1mpLwI.webp 828w, /_astro/mousewheel_1aUQbe.webp 1080w, /_astro/mousewheel_t4NVy.webp 1280w, /_astro/mousewheel_ZdLdi7.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;In our latest update, our engine now directly registers mouse wheel events on the canvas, eliminating the need for manual camera manipulation. This seamless integration brings a host of new features, including smooth zooming, centered around the cursor, and support for pinch gestures on multitouch trackpads. Enjoy a more intuitive and unified interaction experience across various inputs, simplifying and enriching your design process.&lt;/p&gt;
&lt;h3 id=&quot;migrate-to-cesdk-v119&quot;&gt;Migrate to CE.SDK v1.19&lt;/h3&gt;
&lt;p&gt;The structural changes of CE.SDK v1.19 bring a more composable and powerful editing experience. To benefit from our new features, and to attain to licensing changes, head to our &lt;a href=&quot;https://img.ly/docs/cesdk/js/upgrade-4f8715/&quot;&gt;documentation on migrating to v.1.19&lt;/a&gt; for an easy transition.&lt;/p&gt;
&lt;p&gt;Wait. There’s more. As we explore the extensive capabilities of our SDK, we’re unveiling new showcases to demonstrate your possibilities. Plus, stick around until the end for an exciting announcement that will elevate your video creation experience!&lt;/p&gt;
&lt;h3 id=&quot;new-showcase-automated-resizing&quot;&gt;New Showcase: Automated Resizing&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;Scale your marketing materials across all platforms with automated sizes.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/automated_Z1or62o.webp&quot; srcset=&quot;/_astro/automated_1UVbIY.webp 640w, /_astro/automated_1iPdUS.webp 750w, /_astro/automated_1uk67P.webp 828w, /_astro/automated_DqpQ8.webp 1080w, /_astro/automated_2a5INP.webp 1280w, /_astro/automated_Z1or62o.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;In our new live &lt;a href=&quot;https://img.ly/showcases/cesdk/automated-resizing/web&quot;&gt;showcase&lt;/a&gt; for web, effortlessly generate size variations of your designs and seamlessly &lt;strong&gt;scale your&lt;/strong&gt; &lt;strong&gt;marketing materials across platforms&lt;/strong&gt;. The automated resizing feature empowers you to easily leverage marketing materials without requiring the involvement of a design team, ensuring efficiency and adaptability.&lt;/p&gt;
&lt;h3 id=&quot;new-showcase-version-history&quot;&gt;New Showcase: Version History&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;Keep track! Version History monitors changes made to your design.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/history_Z1I9vf0.webp&quot; srcset=&quot;/_astro/history_1suucg.webp 640w, /_astro/history_ZuG7lP.webp 750w, /_astro/history_1T8alE.webp 828w, /_astro/history_Z1gpSmo.webp 1080w, /_astro/history_Z1uhGNc.webp 1280w, /_astro/history_Z1I9vf0.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Discover the convenience of Version History, enabling you to monitor changes made to each design and seamlessly restore past versions when needed. Explore our straightforward &lt;a href=&quot;https://img.ly/showcases/cesdk/version-history/web&quot;&gt;Version History&lt;/a&gt; showcase for a practical design experience.&lt;/p&gt;
&lt;h3 id=&quot;outlook-offer-tiktok-inspired-video-editing&quot;&gt;Outlook: Offer TikTok-Inspired Video Editing&lt;/h3&gt;
&lt;p&gt;Video remains the reigning champion for attracting and retaining your audience. Yet, building a video editor from the ground up is a pain.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-19/HD_IMGLY-SDK_141223-0.mp4&quot; controls playsinline poster=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-19/thumb-vcc.jpg&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;To simplify your journey and save valuable time and resources, we are excited to unveil a groundbreaking SDK for creating captivating short-form videos in January 2024!&lt;/p&gt;
&lt;p&gt;Empower your users to arrange video, audio, text, and graphics seamlessly on a sleek video timeline. Our new camera introduces popular features like Voiceover, Zoom, Tap to Record, and more. Plus, bring the beloved Split Screen Modes for Reactions and Duets, akin to TikTok and Instagram!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://share.hsforms.com/1mrIXiBbURn6sMqYgZG9c6A1hk3i&quot;&gt;Be the first to access our new IMG.LY SDK—join our waitlist now!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading!&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;&lt;strong&gt;Subscribe&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;to our newsletter to stay in the loop with the latest news and updates.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2023/12/v119.jpg" medium="image"/><category>Release Notes</category><category>CE.SDK</category><category>Video Editing</category><category>Design Editor</category><category>App Development</category><category>Mobile App Development</category></item><item><title>CE.SDK v1.14 Release Notes</title><link>https://img.ly/blog/creative-editor-sdk-v_1_14_0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v_1_14_0-release-notes/</guid><description>Experience faster thumbnails for video, enhanced placeholders, macOS support, and more! </description><pubDate>Fri, 25 Aug 2023 15:37:00 GMT</pubDate><content:encoded>&lt;p&gt;Since &lt;a href=&quot;https://img.ly/blog/creative-editor-sdk-v_1_13_0-release-notes/&quot;&gt;our last release&lt;/a&gt;, we’ve been hard at work developing new features and improvements to enhance your creative editing experience. We are excited to announce that CE.SDK v1.14 is now available!&lt;/p&gt;
&lt;p&gt;With this release, you can:&lt;/p&gt;
&lt;h2 id=&quot;generate-thumbnails-fast-for-smooth-performance&quot;&gt;Generate Thumbnails Fast for Smooth Performance&lt;/h2&gt;
&lt;p&gt;Interfaces: Engine&lt;br&gt;
Platforms: All&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-14/thumbnail-improved.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;We have made significant enhancements to the way video thumbnails are generated, resulting in a smoother and more efficient workflow. Our intelligent engine now renders thumbnails seamlessly across multiple frames, ensuring there are no delays or interruptions while you work. Instead of slow JPEG compression, we have implemented a faster method using raw pixel buffers that maintain high-quality results.&lt;/p&gt;
&lt;p&gt;If you’re using web applications, the thumbnail width adapts to fit your screen, providing a seamless and enjoyable user experience. Additionally, our dynamic thumbnail generation adjusts based on the size of your clips and zoom level, optimizing resources and improving overall performance.&lt;/p&gt;
&lt;h2 id=&quot;enhance-templating-with-improved-placeholders&quot;&gt;Enhance Templating with Improved Placeholders&lt;/h2&gt;
&lt;p&gt;Interfaces: Engine, Editor&lt;br&gt;
Platforms: All&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/placeholder-images.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Introducing an enhanced placeholder behavior for text and image content, allowing for seamless editing and replacement. With the new &lt;code&gt;PlaceholderBehavior&lt;/code&gt; concept, you can define whether a block should initially look and behave as a placeholder or allow editing/replacement. This feature provides a smooth editing experience for users and offers flexibility for template designers to differentiate between placeholder and desired content.&lt;/p&gt;
&lt;h2 id=&quot;bring-creative-editing-to-your-macos-app&quot;&gt;Bring Creative Editing to Your macOS App&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;Bring creative editing to your macOS app with CE.SDK.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/macOS_Z2iunHV.webp&quot; srcset=&quot;/_astro/macOS_2uXTJb.webp 640w, /_astro/macOS_Rmfii.webp 750w, /_astro/macOS_Z2okYll.webp 828w, /_astro/macOS_1QoMw8.webp 1080w, /_astro/macOS_Zdxi5T.webp 1280w, /_astro/macOS_Z2iunHV.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;We’re excited to announce the addition of macOS platform support to our lineup. Extend your application’s reach to macOS users and provide a seamless editing experience across different platforms. Get started, and &lt;strong&gt;initialize our engine in your macOS app&lt;/strong&gt; with our &lt;a href=&quot;https://img.ly/docs/cesdk/macos/overview-8cc730/&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;expand-editing-capabilities-macos--catalyst-binding&quot;&gt;Expand Editing Capabilities: macOS &amp;#x26; Catalyst Binding&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;Initialize CE.SDK&amp;#39;s engine in your iOS app, and bring creative editing to your users.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/catalyst_Z1zzDyb.webp&quot; srcset=&quot;/_astro/catalyst_Z16zs6Q.webp 640w, /_astro/catalyst_1SieDm.webp 750w, /_astro/catalyst_20b6IL.webp 828w, /_astro/catalyst_1izTD3.webp 1080w, /_astro/catalyst_2oAGVT.webp 1280w, /_astro/catalyst_Z1zzDyb.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;With this new feature, we provide a comprehensive solution for developers looking to enhance their iOS applications with our CE.SDK’s editing capabilities. Learn how to initialize our creative engine in your iOS app in our &lt;a href=&quot;https://img.ly/docs/cesdk/mac-catalyst/overview-8cc730/&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thank you for reading! Never miss out on updates and&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;&lt;strong&gt;subscribe&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;to our newsletter.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2023/09/ce-sdk_1-14-2.jpg" medium="image"/><category>Release Notes</category><category>Video Editing</category><category>Creative Editor</category><category>CE.SDK</category><category>macOS</category><category>App Development</category></item><item><title>CE.SDK v1.13 Release Notes</title><link>https://img.ly/blog/creative-editor-sdk-v_1_13_0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v_1_13_0-release-notes/</guid><description>Control time and duration of your visuals in videos, enjoy enhanced group and selection behavior, and fine-tune your editor with this new configuration.</description><pubDate>Wed, 19 Jul 2023 13:08:50 GMT</pubDate><content:encoded>&lt;p&gt;Since &lt;a href=&quot;https://img.ly/blog/creative-editor-sdk-v_1_12_0-release-notes/&quot;&gt;our last release&lt;/a&gt;, we’ve been busy crafting new features and enhancements to empower your creative editing journey. Moreover, we have changed the configuration of CreativeEngine and CreativeEditor SDK to make it more versatile. We are excited to announce that CE.SDK v1.13 is now available!&lt;/p&gt;
&lt;h3 id=&quot;enhance-video-composition-duration-and-time-offset-control&quot;&gt;Enhance Video Composition: Duration and Time Offset Control&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Interfaces:&lt;/strong&gt; API&lt;br&gt;
&lt;strong&gt;Platforms:&lt;/strong&gt; All&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/cesdk-113/api-CESDK-time_offset-duration-control.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Whether you want to add a captivating video overlay or include background music, the possibilities are endless. What’s included:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Time Offset and Duration control&lt;/strong&gt;: Give each block a specific time offset and duration, allowing them to appear and disappear at desired intervals. For example, you can overlay a sticker on your video for a brief moment, creating eye-catching effects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API integration&lt;/strong&gt;: Currently, this feature can be controlled via API, providing flexibility and control for advanced customization.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We are thrilled to announce that this engaging feature will soon be available across all our user interfaces. Learn how to configure video and audio in &lt;a href=&quot;https://img.ly/docs/cesdk/js/create-video/control-daba54/&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;elevate-ios-video-experience-with-more-codecs&quot;&gt;Elevate iOS Video Experience with More Codecs&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Interfaces:&lt;/strong&gt; Touch UI&lt;br&gt;
&lt;strong&gt;Platforms:&lt;/strong&gt; iOS&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;H265_codec_iOS.jpg&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/H265_codec_iOS_Z1tRq7A.webp&quot; srcset=&quot;/_astro/H265_codec_iOS_S68en.webp 640w, /_astro/H265_codec_iOS_ZTxBw2.webp 750w, /_astro/H265_codec_iOS_Z2mPwLz.webp 828w, /_astro/H265_codec_iOS_2feqxY.webp 1080w, /_astro/H265_codec_iOS_nb0dc.webp 1280w, /_astro/H265_codec_iOS_Z1tRq7A.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;With CE.SDK’s H.265 support for iOS, you can deliver high-quality video playback on iOS devices while enjoying the benefits of improved video compression and reduced file sizes. Enhance your users’ experience with faster video streaming, enhanced storage efficiency, and exceptional visual quality.&lt;/p&gt;
&lt;h3 id=&quot;easily-select-blocks-in-complex-designs&quot;&gt;Easily Select Blocks in Complex Designs&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Interfaces:&lt;/strong&gt; Default UI, Studio UI, Touch UI, API&lt;br&gt;
&lt;strong&gt;Platforms:&lt;/strong&gt; All&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/cesdk-113/improved-selection-behavior-SDK.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Selecting blocks in complex designs can be tedious and time-consuming. But with our refined selection behavior, we’ve made it effortless. With this enhancement, you can now effortlessly select and interact with elements in your designs, even in the most intricate compositions. Our advanced algorithm considers the shape of the block and the transparency of the fill, ensuring precise and intuitive selections.&lt;/p&gt;
&lt;h3 id=&quot;simplify-group-interaction-and-enhance-mobile-usability&quot;&gt;Simplify Group Interaction and Enhance Mobile Usability&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Interfaces:&lt;/strong&gt; Default UI, Studio UI, Touch UI&lt;br&gt;
&lt;strong&gt;Platforms:&lt;/strong&gt; All&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/cesdk-113/improved-group-selection-SDK.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Simplify the management and editing of groups in your designs with our latest enhancements. Experience a seamless workflow on both desktop and mobile devices, as you navigate and manipulate group elements. With clear hover-states and selection visualizations, group relationships become more apparent, allowing for precise editing and arrangement of elements. Your users can achieve the perfect composition for their designs with ease.&lt;/p&gt;
&lt;h3 id=&quot;configure-via-api&quot;&gt;Configure via API&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Interfaces:&lt;/strong&gt; All&lt;br&gt;
&lt;strong&gt;Platforms:&lt;/strong&gt; Web&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/Configure-API_Z1LvD3K.webp&quot; srcset=&quot;/_astro/Configure-API_Z2nxFW3.webp 640w, /_astro/Configure-API_Z50lxV.webp 750w, /_astro/Configure-API_ZPoKdQ.webp 828w, /_astro/Configure-API_Z1s7cFk.webp 1080w, /_astro/Configure-API_Z1BNUmx.webp 1280w, /_astro/Configure-API_Z1LvD3K.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;We’re thrilled to introduce a more versatile approach to configuring the engine with our latest update. Now, you have the power to fine-tune every aspect of the engine’s settings using comprehensive API calls. This enhanced configurability empowers you to customize your editor precisely according to your requirements. Embrace the flexibility and control to create an editing experience that aligns perfectly with your vision. Learn more about the changes in detail and &lt;a href=&quot;https://img.ly/docs/cesdk/js/upgrade-4f8715/&quot;&gt;how to migrate to CE.SDK v1.13&lt;/a&gt; in our documentation.&lt;/p&gt;
&lt;h3 id=&quot;outlook-easily-remove-backgrounds-in-your-browser&quot;&gt;Outlook: Easily Remove Backgrounds in Your Browser&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/background-remove-open-source_Bj8MG.webp&quot; srcset=&quot;/_astro/background-remove-open-source_Z1H7IwS.webp 640w, /_astro/background-remove-open-source_17jp8H.webp 750w, /_astro/background-remove-open-source_1z2S8x.webp 828w, /_astro/background-remove-open-source_26oJjY.webp 1080w, /_astro/background-remove-open-source_1lQr3P.webp 1280w, /_astro/background-remove-open-source_Bj8MG.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.npmjs.com/package/@imgly/background-removal&quot;&gt;@imgly/background-removal&lt;/a&gt; is a powerful open-source JavaScript library that allows seamlessly background removal of images directly in the browser. With its unique features and capabilities, this package offers enhanced productivity, cost-efficiency, and data privacy while achieving stunning results. &lt;a href=&quot;https://img.ly/showcases/cesdk/background-removal/web&quot;&gt;Try it out&lt;/a&gt; in our background removal showcase. This highly requested feature will soon be available within CE.SDK&lt;/p&gt;
&lt;p&gt;Thank you for reading! 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;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2023/07/creative_editor_sdk-whitelabel-video-editor.jpg" medium="image"/><category>Release Notes</category><category>Releases</category><category>Time Offset</category><category>Video Editing</category><category>Web Development</category></item><item><title>IMG.LY Partners with Soundstripe to Infuse Video Editing with Epic Royalty-Free Music &amp; SFX</title><link>https://img.ly/blog/img-ly-partners-with-soundstripe/</link><guid isPermaLink="true">https://img.ly/blog/img-ly-partners-with-soundstripe/</guid><description>Elevated storytelling and captivating viewers is everything: unlock the ultimate video editing experience with this integration.</description><pubDate>Wed, 24 May 2023 07:31:24 GMT</pubDate><content:encoded>&lt;p&gt;We are excited to announce our exciting new partnership with Soundstripe, the leading provider of royalty-free music for video creators. Our VideoEditor SDK now features a seamless integration with Soundstripe, offering users access to a vast collection of over &lt;strong&gt;9,000 hand-curated songs&lt;/strong&gt; from &lt;strong&gt;150+ musicians&lt;/strong&gt;, along with an extensive library of &lt;strong&gt;70,000 sound effects&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/soundstripe_2.mp4&quot; controls playsinline poster=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/integrate-soundstripe-into-app.jpg&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Starting from version 11, our &lt;a href=&quot;https://img.ly/products/video-sdk&quot;&gt;VideoEditor SDK&lt;/a&gt; now comes pre-equipped with a seamless Soundstripe integration, as detailed in our &lt;a href=&quot;https://img.ly/docs/vesdk/ios/guides/audio-overlays/custom-overlays/soundstripe-integration/#soundstripe-api&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;streamlining-music-licensing-for-digital-platforms&quot;&gt;Streamlining Music Licensing for Digital Platforms&lt;/h3&gt;
&lt;p&gt;Finding the right music for digital platforms hasn’t been easy. Music licensing complexities and the challenge of finding high-quality tracks that align with your brand standards can be overwhelming.&lt;/p&gt;
&lt;p&gt;But now, with our &lt;a href=&quot;https://www.soundstripe.com/blogs/how-to-use-soundstripe-video-0&quot;&gt;partnership with Soundstripe&lt;/a&gt;, you can effortlessly enhance your app with royalty-free music and video editing.&lt;/p&gt;
&lt;p&gt;Our mission has always been to provide the ultimate toolkit for building captivating creative experiences, and video editing plays a central role in achieving this. We recognize the significant role that music plays in video storytelling, as it has the power to evoke emotions, set the mood, tone, and atmosphere, and greatly influence the pacing and energy of a video. With Soundstripe’s diverse range of over 50 genres, your users will have no trouble finding the perfect track to complement their visual narrative.&lt;/p&gt;
&lt;h3 id=&quot;effortless-music-selection&quot;&gt;Effortless Music Selection&lt;/h3&gt;
&lt;p&gt;We have developed an &lt;strong&gt;intuitive search&lt;/strong&gt; interface that allows users to explore music by &lt;strong&gt;title, genre, and description&lt;/strong&gt; effortlessly. Once users have found their desired song, they can easily position any section of the track over their video. Furthermore, we are delighted to provide every one of our customers with seven complimentary sample songs, allowing them to get a taste of the fantastic library available. To unlock the full audio library, simply head over to Soundstripe and &lt;a href=&quot;https://www.soundstripe.com/enterprise-licensing&quot;&gt;request an API key&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We are eagerly looking forward to working closely with Soundstripe to deliver the most exceptional video editing experience on the market. We invite you to try out &lt;a href=&quot;https://img.ly/video-sdk&quot;&gt;VideoEditor SDK&lt;/a&gt; in your Android or iOS app and witness the transformative impact it will have on your users’ creativity. Together, we can take your video editing capabilities to new heights.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! Stay ahead of the curve with our newsletter and &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;sign up&lt;/a&gt; now!&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Jan</dc:creator><media:content url="https://blog.img.ly/2023/06/soundstripe-app.jpg" medium="image"/><category>Video App</category><category>Royalty-Free Music</category><category>App Development</category><category>Mobile App Development</category><category>Video Editing</category><category>Company</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>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></channel></rss>