Version v1.86.0 Changelog November 11, 2026
Breaking Changes
Platform Requirements
- Engine/Swift, Editor/iOS, Camera/iOS: Apple packages now require Xcode 26.6 and Swift 6.3.3 or later. Update your Xcode installation before upgrading these packages.
Angle Units
- Engine: 16 angle properties now take radians by default, like
rotation. They aretransform/skew,transform/skewAxis,text_animator/skew,text_animator/skewAxis,shape/rotation,shape/skew,shape/skewAxis,shape/star/rotation,modifier/repeater/rotation,modifier/twist/angle,fill/stripe/angle,effect/linear_wipe/angle,effect/radial_wipe/startAngle,effect/venetian_blinds/direction,effect/wave_warp/directionandeffect/wave_warp/phase. Before,setFloat,getFloatand their keyframes took and gave degrees. Code that passes degrees converts them. Or it callssetAngleUnit(scene, Degree)to keep degrees for every angle of the scene,rotationincluded. Scene files are unchanged. Scenes saved before and after this change open in either engine with the same angles. - Engine: New scene setting
angleUnit(Radian, the default, orDegree):setAngleUnit/getAngleUnit, thescene/angleUnitproperty and acreateSceneoption. It sets the unit in whichsetFloat,getFloat, the keyframe API,setRotationandgetRotationtake and give angles. The engine keeps angles in radians either way.Degreeis a compatibility option for older integrations: the CE.SDK editor UI expects radians.motionBlur/shutterAngle,motionBlur/shutterPhaseand therotateaction take degrees in either unit. Changing the unit also converts a number variable bound only to angle properties. The HTML5, IDML, PSD, EPS, PPTX, PDF and Lottie importers and exporters work in either unit. Values set and read in degrees round-trip within float precision, so a value can come back a float step off (for example 511.5 as 511.50003). - Engine (Web / Android / Swift):
engine.scene.setAngleUnit('Degree' | 'Radian'),engine.scene.getAngleUnit()and theangleUnitoption ofengine.scene.create(Web);SceneApi.setAngleUnit(AngleUnit),SceneApi.getAngleUnit()and theangleUnitparameter ofSceneApi.create(Android);SceneAPI.setAngleUnit(_:),SceneAPI.getAngleUnit()and theangleUnitparameter ofSceneAPI.create(Swift).
Clip Duration & Timing
- Engine: Added
clip/defaultDurationValue, a double setting in seconds with a default of 5.0 for static clips and empty groups. Groups created from children keep their combined timing. Addedclip/initialDurationModewithdefaultandfollowParentoptions.followParentends foreground static or looping clips with the background track and caps non-looping videos at that end. Explicit durations take precedence. New non-looping videos now use their media duration after loading. Callblock.setDurationto retain an explicit length. Read and write double settings withgetSettingDoubleandsetSettingDoubleon all platforms. - Engine (Android + Swift): Added
ClipInitialDurationModefor use withsetSettingEnumthrough.key(Android) or.rawValue(Swift). - Editor (Web / Android / iOS): Asset insertion now preserves an explicit asset duration, including five seconds. Text and graphic assets without duration metadata use engine timing settings. Audio without duration metadata uses its media duration after loading. Set asset duration metadata or call
block.setDurationwhen an integration needs a fixed length.
Audio Extraction
- Engine:
createAudioFromVideoandcreateAudiosFromVideono longer add an undo step. To make the extraction an undo step, calleditor.addUndoStep()after the call.
Image Source Sets
- Engine:
features/automaticSourceSetsEnabledis now on by default. An image fill you add, or give a new image, gets smaller source set entries. The editor then draws a small image where the full one is not needed. A fill from a loaded scene still gets none. A fill whose state isPending, such as one a plugin works on, gets none until it isReady. While it staysPending, other fills wait for their entries. The engine starts entries only in an update that draws a frame. An engine that draws none, such as a server that only exports, makes none. A browser engine draws a frame on each animation frame, also when its canvas is not on the page. Settingfill/image/imageFileURIto another image now also clears a source set that names the image before. Before, the fill kept drawing the old image.saveToStringnow leaves out each entry that no persistence callback moved, also when you allow thebufferscheme, and succeeds. Before, it reported thebufferscheme, or wrote addresses that only this engine knows when you allowed it. A persistence callback now gets each entry, andsaveToArchiveandfindAllTransientResourcesinclude them. The engine makes the entries over many frames, about 2 ms in each, and one step takes up to 30 ms in Firefox. The entries of a 13 MP photo take about 2 seconds in Chrome on a Mac, and under a minute in Firefox on Windows. Each entry stays in memory for the life of the engine. To keep the previous behavior, setfeatures/automaticSourceSetsEnabledtofalse. On Web and Node.js, useengine.editor.setSettingBool. On Android, useengine.editor.setSettingBoolean. On iOS, useengine.editor.setSettingBool(_:value:).
Export Buffers
- Engine: An export now hands its background engine only the buffers that the scene uses. Before, each export copied every buffer, also one that no block used. A buffer that only
onPreExportor a URI resolver uses no longer reaches that engine. On Android and iOS, create such a buffer on the engine thatonPreExportreceives. A URI resolver may return thebuffer://URI of a buffer that the scene uses, and no otherbuffer://URI. - Engine/Web: A web export in a worker now hands the buffers to the worker without a further copy. The page holds no copy of them while the export runs.
Page Fills
- Engine: An image or video fill of a page now covers the bleed, not only the page. So a saved scene with a bleed shows a slightly larger image, and a crop scale ratio of 1 covers the page and its bleed. To keep the old behavior, set the setting
page/imageFillCoversBleedto false.
Variables
- Engine: A loaded scene or archive no longer fills variables into file paths. Only
{{scene_root}}is replaced, so a path such as{{scene_root}}/{{first_name}}.pngkeeps{{first_name}}as written. - Engine: Variable names
scene_rootand names starting withubq.are reserved for the built-in placeholders of file paths and page titles. Creating or renaming a variable to such a name fails. A name that is empty, starts or ends with whitespace, or contains{{or}}fails too. - Engine: A loaded scene repairs each variable name that is not valid, with a warning. It trims the whitespace, replaces
{{with{and}}with}, and turnsubq.intoubq_. A taken name gets the first free(N), soscene_rootbecomesscene_root (1). A variable whose name has nothing left, such as an empty one, is removed with its bindings. The bindings, text tokens and page titles that use a renamed variable follow it. A name inside a longer expression or a statement, such as{% if name %}, is not renamed. On Web and Android the warning reaches your log. - Engine: Each scene keeps its own variables, and undo and redo restore their changes. Loading a scene replaces the variables of the previous scene, so a variable that you set on the previous scene is gone after the load. Set the variables again after each load.
- Engine: A variable that you set while no scene exists waits for the next scene. The next created or loaded scene takes it as a setter without a target writes it. That is the active value set when the set holds the name, else the default. A value of another type than the loaded variable is dropped with a warning. On Web and Android the warning reaches your log. Set variables before
loadSceneto fill a template this way. - Engine:
applyTemplateFromStringandapplyTemplateFromURLkeep the variables, the value sets and the active set of the current scene. The scene gains the variables and value sets of the template that it lacks. The template’s sets lose their values for the names that the scene holds. Before, the current variables stayed, and the variables of the template were dropped. - Engine:
findAllVariablesandfindAllVariablesOfTypelist the variables of the scene, or the waiting variables while no scene exists. - Engine: Loading blocks writes the variable values of the scene into their bound properties.
- Engine: Scenes save their variables in the scene block, and they also keep the variables in the place that engines before this release read. Those engines read them only with
overrideEditorConfigset to true. They show the values of the active value set in text, and they cannot read the value sets themselves. When such an engine saves the scene again, it keeps only those shown values. This engine then loads them as the default values, and the value sets are gone. - Engine:
cesdk_clisets the variables of--text key=valueafter each scene load, before the export. So every--repeatshows the values, and a value replaces the value that the scene shows for its name. Before, the scene variable won. A reserved key, such asubq.titleorscene_root, ends the run with an error. A key that the scene holds as another type, such as a color variable, now ends the run with an error too. Before, the run exported the scene. - Renderer:
--text key=valueworks as incesdk_cli. The Renderer sets the values after each scene load, so every--repeatshows them. A key that the scene holds as another type now ends the run with an error. Before, the Renderer exported the scene.
Deprecations
All items below remain functional this release; migrate before they are removed.
- Engine/Swift:
setBackgroundColor(_:r:g:b:a:)andgetBackgroundColor(_:)are deprecated. UsesetColor(_:property:color:)andgetColor(_:property:) -> Colorwith the propertybackgroundColor/colorinstead. - Engine: Deprecated
loadCMYKProfile,setCMYKProfile,setCMYKProfileFromData,getCMYKProfileInfoandremoveCMYKProfile. UseloadColorProfilesand the document color profile methods with the Default CMYK slot.loadCMYKProfilenow also waits for a profile that is still downloading in another slot. - Engine/Node Native:
block.exportVideoignores itsuseTargetSizeoption, which is now deprecated. As on the web, the export uses the target size whentargetWidthortargetHeightis above 0.
New Features
Color Profiles
- Engine: Added document color profile APIs to assign, inspect and remove separate Default and Output profiles for RGB, CMYK and Gray. Canceling an assignment keeps the previous profile. Profiles persist in scenes and archives. Engines older than this release ignore Output profiles and drop them when they save the scene. Only Default CMYK currently affects color conversion; the other slots store profiles for later use.
- Engine: Added
loadColorProfilesto wait until the profiles used for color conversion are loaded. It opens every profile the document names and the Default CMYK fallback, sogetDocumentColorProfileInfothen reports theirname. It fails only when the Default CMYK profile cannot be read. A profile that is still downloading in another slot delays the callback.
Variable Value Sets
- Engine: Added value sets: named sets of alternative values for the scene variables, such as “Long names” or “Deutsch”. Use
createValueSet,destroyValueSet,setValueSetName,getValueSetName,findAllValueSetsandfindValueSetByName. A value set id is a string that the scene saves. It stays the same across saves, loads and renames. A value set name must not be empty and must not start or end with whitespace. A loaded scene repairs a set name that is not valid or taken. A valid name keeps its set, and a repaired name gets the first free(N). It removes a set without a unique id and a value that its setter refuses. It logs a warning for each fix. On Web and Android the warning reaches your log. - Engine: The variable setters, getters and
removeVariabletake an optionalVariableOptions::target: the default value or a value set. Without a target, a call uses the active value set when it holds the name, else the default value. A getter then returns what a setter wrote. - Engine:
setActiveValueSetshows the values of one set in text, page titles, bound properties and getters. A variable that the set has no value for shows its default. Exports show the active set, as the canvas does. The scene saves its sets and the active set, and undo reverts their changes. - Engine: Changing the design unit converts a number variable in its default value and in every value set, when its bound lengths follow its shown value.
- Engine (Web / Android / Swift): Added value sets to
engine.variable: named sets of alternative variable values that the scene saves. Web:engine.variable.createValueSet,destroyValueSet,setValueSetName,getValueSetName,findAllValueSets,findValueSetByName,setActiveValueSetandgetActiveValueSet. Android:createValueSet,findValueSetByName,setActiveValueSetand the rest. Swift:createValueSet(name:),findValueSet(byName:),setActiveValueSet(_:)and the rest. A value set ID is a string (Stringon Android and Swift) that the scene saves, and it stays the same across saves and loads. - Engine (Web / Android / Swift): The variable setters, getters and remove take an optional target: a last argument
{ target }with'Default'or{ valueSet: id }, includingengine.variable.remove(Web);VariableTarget.DefaultorVariableTarget.ValueSet(id), includingremove(Android);.defaultValueor.valueSet(id), includingremove(key:)(Swift). Without a target, a call uses the active value set when it holds the key, else the default value, sogetStringreturns whatsetStringwrote. - Engine/Node: Added the same value sets and
{ target }option to@cesdk/node-native.
WebM, Opus and Vorbis
- Engine: WebM videos with VP8, VP9 or AV1 video and Opus or Vorbis audio now load and play. Android, iOS, macOS, Linux and Windows decode them with codecs that the engine bundles. On Android, iOS and macOS, the hardware decoder of the device takes VP8, VP9 or AV1 where it has one. The web decodes them with the codecs of the browser.
- Engine: Video export now writes WebM files (
video/webm) with VP8, VP9 or AV1 video and Opus or Vorbis audio. Android encodes VP8 and VP9 with a hardware encoder where the device has one. The newvideoCodecexport option selects the video codec. A WebM export uses VP9 and Opus by default. - Engine: The
audioCodecexport option now also accepts Vorbis. A WebM export accepts Opus and Vorbis, and an MP4 export accepts AAC and Opus. Vorbis export is not available on the web. - Engine: The new
exportAlphaexport option keeps the transparency of the page in a VP8 or VP9 WebM video. It is not available on the web. - Engine: Android, iOS, macOS, Linux and Windows now encode Opus audio, in MP4 and in WebM exports, and play MP4 files with Opus audio.
- Engine/Web: Audio export now writes WebM files (
audio/webm) with Opus audio. - Engine/Node: Audio export now writes WebM files (
audio/webm), and the native Node.js engine now encodes Opus. - Engine (Swift + Android):
VideoExportOptions(Swift) andExportVideoOptions(Android) have the newvideoCodec,audioCodecandexportAlphaparameters.MIMETypehas the new.webmand.webmAudiocases (Swift), andMimeTypehas the newWEBMvalue (Android).
Memory Management
- Engine: Added
engine.editor.releaseMemory(), which frees the memory that the engine can build again without a download. It frees the decoded images that the canvas did not draw last, the video thumbnails, the unused files and the unused GPU textures. - Engine/Swift: On iOS the engine calls
releaseMemory()on its own when the system sends a memory warning. - Engine/Android: The engine calls
releaseMemory()on its own when the system reports low memory or moves the app to the background. - Engine: Add the
features/historyEnabledsetting. Set it tofalsefor an engine that never undoes, such as one that only exports. The engine then keeps no copy of the scene for undo, loads a scene faster, andaddUndoStep,undoandredochange nothing.setActiveHistorystill returns each history to its scene. The default istrue. A saved scene does not keep this setting.
Voice Isolation
- Editor/Android: The new
IMGLYVoiceIsolationPluginadds an “Isolate Voice” button for video and audio clips. It removes music and background noise on the device and keeps the voice. Addly.img:plugin-voice-isolation-imgly, or useVoiceIsolationPluginfromly.img:plugin-voice-isolationwith your ownVoiceIsolator. CallisolateVoiceto clean a clip from your code. The plugin downloads the 8.6 MB model from the IMG.LY CDN when the editor opens. - Plugins/iOS: Add the
IMGLYPluginVoiceIsolationplugin, which removes music and background noise from the speech in video and audio clips, on the device.VoiceIsolationPluginadds an “Isolate Voice” button to the inspector bar of the selected clip. - Plugins/iOS: A video clip first gets the audio track that it plays separated into a new audio clip, as with “Separate Audio”. One undo step brings the original audio back.
- Plugins/iOS: The plugin downloads its 8.6 MB model when the editor opens. To ship the model with your app, set a
file://model URL. A cleaned clip plays a local WAV file. - Plugins/iOS:
isolateVoice(of:engine:)on the plugin instance cleans a clip from code, for example inonCreate.VoiceIsolationProviderplugs in your own model or service.
Background Removal
- Editor (Android + iOS): The
BackgroundRemovalPluginoffers a “Remove Background” button for the inspector bar, which removes the background of the selected image or page. Add the button withinspectorBarModifier(on Android, addInspectorBar.Button.rememberBackgroundRemoval).
Transitions
- Editor (Web / Android / iOS): The transitions library offers Clock Wipe, with duration and easing settings.
- Engine: The
ly.img.transitionsasset source lists thely.img.transition.clock-wipepreset.
Timeline
- Editor/Web: The video timeline shows a notification when a move or a trim removes a transition, because its two clips no longer meet. A small move that keeps the clips overlapping keeps the transition and shows nothing. The new translation key is
notification.transitions.removedSeparated.
Text
- Editor/Web: The advanced text panel for text and captions has new Script controls: Normal, Superscript and Subscript buttons. When you select characters while editing text, the buttons change only those characters. The
ly.img.text.advancedfeature key shows them with the rest of the panel.
Style Presets
- Engine: A style preset can list properties in
keepExistingthatapplyToBlockdoes not change, such astypefaceto keep the font. A block thatapplycreates from the preset still takes them. A preset without the list keeps the block’s text, as before.
Animation
- Engine:
setKeyframesnow takestransform/scalewithVec2keyframes and writes thetransform/scale/xandtransform/scale/ytracks at once.getKeyframesandgetKeyframeModeread the two back as one track while they share their times and easings, and fail with an error otherwise.removeKeyframesremoves both. Before,transform/scalewas refused as not animatable.
Canvas Interaction
- Engine: Added the setting
features/transformLockedClickThroughEnabled, off by default. When it is on, the canvas skips a block with a locked transform and the blocks inside it. Clicks, taps, hover and marquee selection reach the block below. The API still selects a locked block, for example from a layer list. A locked page still takes clicks, and the blocks on it stay reachable.
Downloads
- Engine: Add the
maxConcurrentRequestsPerHostsetting. It limits how many images, fonts and videos the engine downloads at the same time from one host. Use it for a server that rejects too many requests at once. A further download waits until one of them ends. Each engine counts its own downloads, and an export copies the setting into its export engine. The Renderer takes it as--max-concurrent-requests-per-host. The default is 0, which sets no limit, as before. In the browser and in@cesdk/node, a video that streams in parts counts only until its first parts load.
Export
- Engine: Added
exportPreview, which exports a small WebP image of a block. A preview does not count toward export usage. - Plugins/Web: Added
maxInputSizeto print-ready PDF conversion options so applications can convert PDFs above the default 100 MiB input limit.
Starter Kits
- Editor/Web: The Photobook Editor starter kit offers an A4 landscape book (29.7 × 21 cm). It has its own layouts and its own Playful and Chic style scenes.
- Engine/Web: Add a headless starter kit with five editable JavaScript examples and a design preview exported on click, without mounting an editor.
Improvements & Behavior Changes
Color Management
- Engine: A change of the rendering intent or the black point compensation no longer resets blocks that wait for a color profile. It requests one render.
- Engine: Reading an empty profile slot fails with the message “The document has no profile in the requested slot”.
getCMYKProfileInfoused to fail with “The document has no CMYK profile”. - Engine:
findAllTransientResourcesnow reports a document color profile that is stored as abuffer://URL, andrelocateResourcerepoints it. AsetDocumentColorProfilerequest that is still loading when its URL is relocated now commits the new URL.
Background Removal
- Editor (Android + iOS): The
BackgroundRemovalPlugindock button only shows when the user may change the current page’s image. Neither button shows for a sticker or for an SVG image. - Editor/Android: The
BackgroundRemovalPluginnow writes the result into the image’s own fill and replaces every resolution of its source set. Library images show the result at every zoom level. As on web, this resets the crop of such an image. - Editor/Android: The
BackgroundRemovalPluginnow queues a removal for each tapped image. Before, a new removal cancelled the one that ran. - Editor/iOS: The
BackgroundRemovalPluginnow replaces every resolution of an image’s source set, so library images show the result at every zoom level. As on web, this resets the crop of such an image. - Editor/iOS: The
BackgroundRemovalPluginnow processes a rotated photo, such as a portrait camera photo, at its own size. Before, it scaled the photo up by the screen scale first. - Editor/iOS: The
BackgroundRemovalPluginnow queues a removal for each tapped image. Before, a tap while another removal ran was ignored. - Editor/iOS: The
IMGLYPluginBackgroundRemovalSwift package now gets ONNX Runtime from the newIMGLYONNXRuntime-swiftpackage, which Swift Package Manager adds by itself. An app that mirrors its Swift packages also mirrorsimgly/IMGLYONNXRuntime-swiftfrom GitHub. CocoaPods does not change.
Memory
- Engine/Android:
setAppIsPaused(paused = true)now also frees the memory thatreleaseMemory()frees. So after a resume, the first frames upload their textures again and make the video thumbnails again. - Engine: The video thumbnails of a scene no longer stay in memory after another scene loads, or after the engine removes their video.
- Engine: The GPU cache now keeps at most half of the memory that it could reach, as the other caches do. Before, it grew until no memory was free. This applies on Android and on iOS before 16.
- Engine: When memory runs low, a scene loaded from an archive now keeps the decoded images of its files instead of decoding them again. Files in a buffer no longer count against the engine’s memory budget, because the buffer keeps their bytes. Scrolling a 119-page photobook archive with 48 MB of free memory decodes a third fewer images.
- Engine: When an image export runs short of memory, the engine now frees decoded images from its caches and checks the memory again. Before, it failed with “Not enough resources available on device” while those images stayed in memory. On iOS and Android, the images that the GPU holds stay.
- Engine:
addImageFileURIToSourceSet, and a source set entry without a width or a height, now read only the size of the image file. They no longer decode the pixels of the image and keep them in memory. An image abovemaxImageSizenow gets its real width and height in the source set, not the size of a copy reduced tomaxImageSize. Creating a scene from an image no longer decodes its pixels either, and its page keeps its size. - Engine: A video clip holds its video and audio decoders only while it plays within 1 second of the playhead. Memory during playback and video export grows with the clips near the playhead, not with every clip in the scene. A clip that the platform cannot decode shows its error when it comes within 1 second of the playhead, not when the scene loads.
- Engine: The undo history keeps each block compressed, so it needs much less memory. A loaded 300-page magazine scene keeps 4 MB for undo instead of 75 MB. The history also drops its oldest undo steps when the states they keep hold more than 64 MiB.
- Engine/Web: The export workers turn off their undo history, so an export needs less memory and loads its scene faster.
Video & Audio
- Engine: A 10-bit VP9 or AV1 video now shows with SDR colors on Android, iOS, macOS, Linux and Windows.
- Engine: An audio export now fails when the platform cannot decode the sound of a block that the export plays, as a video export does. The error names the block. Before, the export wrote silence for that block.
- Engine/Node: The WASM engine has no AAC or MP3 decoder in Node.js. So an audio export with an AAC or MP3 clip now fails with that error instead of being silent.
- Engine:
getMimeTypenow reads an OGG file by its content, whatever its file extension:video/oggwhen it holds video,audio/oggotherwise. It returnsaudio/flacfor a FLAC file, where it returnedunknown. An asset with the MIME typeaudio/flacand no block type is now rejected, likeaudio/ogg. - Engine/Web: Video playback and video export in Chrome, Edge and Safari use less GPU memory. Each H.264 or H.265 video keeps as many decoded frames as its stream needs, instead of 17. A 1080p H.264 video at level 4 keeps 6 frames in Chrome, which is 50 MB instead of 141 MB. Safari keeps 9 frames for the same video with B-frames. Firefox, Android browsers, and a video whose decoder needs more frames keep 17. A 60 fps video keeps 2 frames more, so that it plays smoothly. A video also keeps 17 when an export or a playback skips its frames. An example is a 30 fps video in a 10 fps export or at twice its speed.
- Engine/Web: Video playback keeps moving when the browser decodes video more slowly than it plays, such as four 1080p videos in Firefox. Before, the video showed one frame until decoding caught up.
- Engine/Web: While the editor plays a scene, blocks that show one video file at the same time decode each frame once, in every browser except Safari. An example is a video with a blurred copy behind it.
Export
- Engine: Exports, thumbnails and cutouts now render through their own view instead of moving the camera. A camera move you make during a video export now stays when the export ends.
- Engine: An export now counts toward export usage only when it completes. A failed or canceled export isn’t counted. On the web, an image or PDF export counts once the engine has encoded it, and a large PDF, a video or an audio file counts once your code receives it.
- Engine (Web / Node / Node Native):
block.exportandblock.exportWithColorMasknow scale the block when onlytargetWidthor onlytargetHeightis set, as on Android and iOS. The other dimension follows from the block’s aspect ratio. Before, the export kept the block’s size. A target that is not a finite number above 0 counts as not set. - Engine (Web / Node Native):
block.exportVideonow also scales the block when only one of the two values is set. - Engine/Web: A video export now writes the video into a file in the site’s storage and returns a Blob of that file. The export no longer holds the whole video in memory, and the page gets no copy of it. A PDF export in a worker does the same.
- Engine/Web: Where the browser offers no such storage, the export returns the video from memory as before. The export fails when the storage cannot hold the file.
- Engine/Web: The file counts against the site’s storage until the engine removes it, at its first start after 7 days.
- Engine/Web: An export in a worker now hands its result to the page without a further copy.
- Engine (Android + Swift): An export now reuses the background engine of the export before it. A second export of an unchanged scene does not fetch or copy its images and fonts again. A scene with video or audio still gets a new engine for each export, because its decoders would stay with the engine. The engine stays for 30 seconds after an export, or until the main engine stops (Android) or is released (Swift).
onPreExportruns on every export, and what it changes stays for the next export, such as settings, asset sources and event subscriptions. - Engine: A PDF export with
exportPdfWithHighCompatibility, the default, keeps a linear or radial gradient without transparency as a vector. The file is smaller, and a large page with such a gradient no longer fails to export as too large for the device. Images, stripe fills, conical gradients, gradients with transparency and fills with effects are still drawn as pixels.
Resource Loading
- Engine:
forceLoadResourcesnow fails when an image fill’s file loads but cannot be decoded, such as a broken SVG or a corrupt PNG. The block reports anImageDecodingerror at once. Before, the call succeeded, and the error showed only when the block was drawn or exported. The load does not check a CMYK JPEG that has no color profile of its own. On Web and Node.js,addImagenow fails for such a file too. - Engine: A scene whose pages you create in code now loads only the images and fonts of the pages in or next to the view. Before, the first update after you added the pages loaded those of every page, whatever the camera showed. A read of a block on another page, such as its size or its text lines, still returns the laid-out value.
- Engine/Swift: The engine now takes the size of the canvas when the canvas gets its size and when you create a scene. Before, it waited for the next drawn frame, and every page loaded its images and fonts until then.
- Engine/Android: The engine now takes the size of the canvas when its surface gets its size and when you create a scene. Before, it waited for the next drawn frame.
Fonts & Text
- Engine: A font weight given by its OpenType name, such as
Black,RegularorDemi Bold, now keeps its weight in typefaces and asset sources. Letter case and the separator between words do not matter. An unknown weight name in a typeface, an asset source orsetTextFontWeightnow fails with an error that names it. Before, a typeface or an asset source changed such a name tonormalwithout a message. A style preset still appliesnormalfor an unknown weight name. - Engine: The
pathandcasekeys of a style preset now work without thetext/orcaption/prefix. - Engine: The dots and dashes of a text outline stay in place while you type. Before, each new letter moved them on the earlier letters of the line. In existing designs, they can start at another point on a letter.
Strokes
- Engine: A dashed or dotted stroke style now keeps its dash proportions when the stroke width changes, also for an animated width. In the web editor, the Style menu keeps the style instead of switching to Custom. A dash pattern that matches no preset keeps its lengths. On a closed shape, the number of dashes can change while the width changes. A stroke saved with short dashes keeps them until its style is picked again.
SVG Support
- Engine: SVG files whose DOCTYPE declares entities, as Adobe Illustrator exports do, now load. A file with an external or parameter entity, or with an entity that refers to another entity, still fails to load. So does a file whose entities make it more than 4 times larger, beyond 8 MB.
- Engine: A raster image embedded in an SVG now loads from any base64
data:URI, whatever its media type and letter case. Examples aredata:img/png;base64,andDATA:IMAGE/PNG;BASE64,. - Engine: SVG pattern fills now follow
patternUnits,patternContentUnitsandviewBox. So an image fill exported from Figma shows the image instead of one flat colour, and Adobe Illustrator swatches draw. As in browsers, a pattern withoutpatternUnitssizes its tile relative to the filled shape, and the pattern content starts at the tile corner. Patterns, gradients, masks, filters and clip paths sized relative to the shape now also apply to ellipses and lines. Such a pattern on a shape without width or height, such as a horizontal line, draws nothing. - Engine: SVG
<use>,<image>, gradients, patterns and text paths now follow the SVG 2hrefattribute as well asxlink:href. When an element has both,hrefwins. - Engine: SVG files with
<style>elements now load and apply their CSS rules from every<style>element, as browsers do. An element with both aclassand astyleattribute no longer fails to load. Type, class, ID and*selectors apply, also combined, in comma-separated lists and with descendant and child combinators. Rules inside@media screenand@media allapply too. Thestyleattribute now wins over presentation attributes such asfill. An element whose rules set no fill keeps its own or inherited fill. Before, it turned white. A file whose rules would need more than about 33 million matching steps fails to load, so that a crafted file cannot stall the engine. - Engine: SVG content inside
<switch>now renders, so Adobe Illustrator files no longer load empty. A<switch>draws its first child that the engine can draw, that has norequiredExtensions, and whosesystemLanguage, if set, includes English. An element whoserequiredExtensionsorsystemLanguagefails is no longer drawn, also through<use>.
Asset Sources
- Engine: The
ly.img.animationsandly.img.transitionsasset sources now return the English label for a locale without a translation, instead of an empty label. A query without a locale also gets the English label. - Engine:
fetchAsseton thely.img.scene.colorsasset source now returns the colors of the current scene, labelled with their color name as infindAssets. The source can also sort its colors by label. - Engine: The
ly.img.transitionsasset source now leaves out themorphproperty when a text clip is on either side of the cut. Morph applies only between two graphic clips. The transition settings on Web, Android and iOS no longer show a Morph control without effect. - Editor/Web: The tooltip and accessible name of an asset library card now show the
cardLabelof its entry, like the card itself.
Variables & Page Titles
- Engine: A page title fills a variable whose name holds
/or a space, such as{{brand/color}}or{{First Name}}. - Engine: A page title holds at most 4 KiB. A longer title is cut, and it still ends with the page name when the name is appended.
Timeline & Captions
- Editor (Android + iOS): The timeline message about removed transitions now also shows after a trim, a reorder, and As Clip or As Overlay. It also shows after a move along the same track that leaves a gap at a transition.
- Editor (Web / Android / iOS): A clip that you drag on the video timeline now shows its full length, and so does its drop preview. The clips that it shares a transition with show the end that meets it in full. Before, the drag showed the shorter lengths, so a short drag could leave a gap between two clips while their transition still played.
- Editor/Web: Moving or trimming a clip on the video timeline changes only that clip and the rows it moves between. Empty tracks the edit did not empty stay, other clips keep their trim, and the background and caption tracks keep their place in the scene.
- Editor/Web: ArrowUp and ArrowDown now move a video clip in the timeline past an audio row, as a drag can. An audio clip passes a video row the same way. The clip lands in a new row beyond it. Before, the clip stopped at that row.
- Editor/Web: “Merge with previous” in the timeline and caption panel menus is disabled on a caption with no caption before it in its own track. A keyboard trim at a clip’s minimum length no longer adds an undo step.
- Editor/Web: “Add after” on a caption adds the new caption to the same track, and is disabled when the next caption leaves no room.
- Editor/Web: The timeline clip menu no longer offers Mute or Unmute for a clip that plays faster than 3x. A clip plays no audio at that speed.
- Editor/Web: Space after clicking a timeline clip now plays and pauses the video. A clip reached with Tab still selects on Space and Enter.
- Editor/Web:
timeline.setHeightwith aheightnow replaces a timeline height that the user resized. - Editor/Android: The timeline shows an audio or video clip as loading while you set its block to
BlockState.Pending, for example while a plugin processes it. Users cannot trim the clip during that time. - Editor/iOS: The timeline shows the loading state on an audio or video clip while its block is pending, also when the clip is selected.
Editor UI
- Editor/Android:
EditorState.viewModeemits a new value after a window resize in preview mode. A configuration that zooms in preview can then fit the new window, as the Apparel, Photo and Postcard starter kits do. - Editor/Web: The placeholder option for animations reads “Allow to Change Animations and Transitions”, because it also controls transitions.
- Editor/Web: Replacing a video with a still image now shows an error and keeps the video. Before, the video turned into a broken placeholder. Animated PNGs and GIFs still replace a video.
- Editor/Web: The undo and redo notifications of Separate Audio now name the action.
- Engine: A tap now selects a block whose stroke lies up to 6 logical pixels away, unless the tap lands on another block. A thin shape outline or the curve of a selected text on a path is easier to hit with a finger. Mouse input does not change.
AI Plugins
- Plugins/Web: An image-to-image
GatewayProvidermodel now sizes the new image block to the format you choose. Before, the block took the source image’s aspect ratio and cropped the result. A format without a fixed size, such as “Match input”, still uses the source image’s size. Text-to-image now also sizes the block to ratio presets such as 21:9. Before, it made a square block.
Renderer
- Renderer: The Renderer images now run the engine of
@cesdk/node-nativeon Node.js 22, which ships inside the images and makes each download about 30 MB larger. Existing command lines and licenses keep working with the same output. - Renderer: The Renderer turns off its undo history, so it needs less memory and loads a scene faster.
- Renderer: An interrupt that arrives between two exports now ends the run with exit code 1.
- Renderer: An input path that is not valid UTF-8 now fails the input check with exit code 105. Before, it failed to load with exit code 1.
Performance
- Engine: The sort of a
findAssetsquery now takes about a third of the time by label, and about half by a meta key. - Engine: On Linux, the update loop takes up to 8% less time, and a scene of 300 pages loads up to 3% faster.
- Engine: On macOS, an idle update of a large photobook takes up to 72% less time, and scrolling it up to 9% less. This holds while no video is converting and no conversion has failed. iOS runs the same code.
- Engine: Blocks with a drop shadow, translucent blocks with a stroke and translucent groups render faster. Their layers now cover the block, not the whole page. Stroked text and text with a drop shadow at full opacity draw in one layer fewer.
- Engine: Destroying blocks without text, and undoing a step that added them, is up to about twice as fast in a scene with 2000 text blocks.
- Engine: Duplicating and pasting blocks is faster. On Linux, one page of a 300-page scene duplicates and pastes more than twice as fast.
- Engine: Scenes that embed large images as
data:URIs draw faster, and use less memory while they draw. - Engine: A Lottie video fill whose file holds very many objects in one list, such as layers or keyframes, loads faster. On Linux, a list of 100,000 objects took more than 14 seconds to load, and now takes less than a tenth of a second.
- Engine: A Lottie video fill whose file uses slots loads faster and needs less memory. On Linux, a 29 MB file took about 1.5 seconds to load, and now takes about 0.13 seconds.
- Engine:
getStateno longer updates the engine on each call for a block that nothing draws, such as a block not yet appended. - Engine: Moving the pointer over a scene with many pages runs faster. The engine no longer tests pages whose blocks are all away from the pointer.
- Engine: A read of the frame, bounding box, crop or text runs of a block no longer updates the engine when nothing changed. A loop that reads every block of a laid-out scene now runs no update.
- Engine: Transitions, motion blur and a preview that
maxPreviewResolutionlimits now reuse GPU textures of the same size from frame to frame. Playback and video export no longer allocate new GPU textures for these effects in each frame. This applies on the web, Android, iOS, macOS and Linux. - Engine: Loading a scene or applying a template from a string, a URL or an archive needs less memory. For a scene with 300 pages, the peak memory of the load falls by 63 to 125 MB.
- Engine: Saving a scene to a string needs less memory. On Linux, the peak memory of a process that loads and saves a 300-page scene falls by 46% without compression and by 24% with Zstd. A save without compression also takes half the time.
- Engine: Scrolling a scene with many pages runs faster. A scroll no longer lays out the scene again. An edit now updates the click and hover areas of the changed blocks only, and a zoom only those of the pages in view.
- Engine: A layout no longer sends an
Updatedblock event for a block whose size and position it did not change. A block that moves only with its parent sends no event. - Engine: When you zoom into part of a page, the canvas no longer draws or decodes the images of that page outside the view. A shadow, a stroke or a blur that reaches into the view still draws.
- Engine: Text blocks need less memory, and typing in a long text takes less time. Before, each laid-out text block kept an extra copy of its layout.
- Engine: Cutouts, boolean combine and PDF underlayers of long text take far less time. On an Apple M3 Pro, a cutout of 1,600 characters took 43 s and now takes 34 ms.
- Engine: Text with an Inner, Outer or dotted stroke draws faster. On an Apple M3 Pro, one draw of 20,000 characters with an Outer stroke took 970 ms and now takes 110 ms.
- Engine: Undo and redo that restore many blocks take less time in a large scene.
- Engine: Undo and redo of a change to many blocks take less time in scenes with many pages. Before, each restored block checked the titles and borders of all pages.
- Engine: Undo and redo of an edit in a long text take less time.
Bug Fixes
Engine
- A block no longer stays loading when its ICC profile finished loading but cannot be read.
- A block that waits for the ICC profile of an image no longer also waits for the document color profile.
- A block that waits for a CMYK profile keeps the correct loading state after a scene load fails.
- On iOS and macOS, Node Native included, a sound at the start of an AAC file now plays from its first frame at full level. Before, it started about 10 ms late and quieter.
- The first frame of a trimmed AAC clip now plays at full level on iOS, macOS and Linux, Node Native included. Before, it faded in.
- H.264 videos up to 576 lines tall without a color tag now show their true colors on iOS and macOS. So do videos tagged BT.601 in their file, and H.264 videos tagged BT.601 in their stream. Saturated colors in these videos looked shifted before.
- An audio block now plays an MP4 file of a less common brand, such as a 3GP recording with AAC audio, from a
buffer://resource. A buffer that holds an OGG or FLAC file now reports a decoding error. Before, both played noise and drew a noise waveform. fetchAsseton thely.img.animationsasset source now finds an animation before the firstfindAssetscall.- An audio block whose buffer holds no data no longer keeps the engine busy after
getAVResourceTotalDuration, the trim calls,setDuration,setPlaybackSpeedorsplit. Before, the engine updated on every frame until the buffer got data, for example a voiceover before its first samples. - An audio block whose buffer holds no data no longer keeps the engine busy after an MP4 audio export. Before, the engine updated on every frame until the buffer got data.
- On iOS, macOS and Android, audio from a buffer no longer crashes the app when a buffer is created or destroyed while the audio plays.
- The type check of a short UTF-16 subtitle file no longer reads past the end of its data.
- An MP4 video with a damaged HEVC configuration record now opens, instead of stopping the engine. A video whose time scale is 0 or exceeds 2,147,483,647 units per second now fails to open with an error. A time after the last frame of a long video shows its last frame.
- An animated PNG that declares more frames than it holds now loads, instead of stopping the engine.
- A compressed scene whose header states more bytes than its data can hold now fails to load with an error, instead of stopping the engine. A compressed scene whose header states no size now loads, also when it holds several compressed parts.
- A JPEG image that shows at less than 1/4104 of its width and height now shows in its colors. It showed black before.
- A scene or block string with a damaged block structure now fails to load with an error, and the open scene stays loaded. The damage can be a missing block, a loop of parents, a block listed twice, or two blocks with one id. A scene whose root is not a scene fails too. Before, the engine stopped, froze or loaded a block twice.
- A scene in which a block names a fill, shape, effect or other block that the scene does not hold now loads without that reference. Its page still exports. Before,
getFilland its siblings returned an id that no block has. - A text whose saved style runs lie outside the text now loads in the style of its block, instead of stopping the engine.
- A scene archive whose scene or blocks fail to load no longer keeps its files in memory.
*in an asset sourcematcherpattern now matches any characters, line breaks included.{and}match only themselves. Before, a pattern with{or}crashed the engine or matched other asset IDs. A pattern with two or more*could crash the engine on a long asset ID.- Duplicating a block whose name ends in a very large counter, such as “Photo (99999999999)”, no longer crashes the engine. The duplicate is named “Photo (99999999999) (2)”.
- The duplicate of a block whose name holds a line break now continues the counter at the end of that name.
- A
scene/dpichange in a pixel scene now scales the font sizes of the existing text right away. Text written after the change keeps its font size. Before, the next engine update rescaled that text as well. A DPI change followed by a design unit change, before the next update, now also keeps the text size on the page. getMimeTypenow returns the MIME type of a file that loaded but that the engine cannot decode. An example is a video or audio file in an archive that@cesdk/nodeloads. Before, the call failed with “Error fetching resource”.findAllTransientResourcesnow lists such a file, andgetResourceDatareads it, so you can move it to a new location before a save.- A URI resolver no longer causes
shader not loadederrors while the built-in shaders load. A shader file that fails to load now logs an error that names the file. - Zooming to a block now applies small zoom changes. A change below 0.001 was dropped before, so after a small layout change the page could stay up to 1 px off its fit.
- After a PDF export with
exportPdfWithDeviceCMYK, the canvas and thumbnails draw spot colors with their RGB approximation again. Before, they drew the CMYK approximation until the next export without that option. - After
forceLoadResources, a block keeps the error of a file that failed, also when another file of the block, such as its preview, loads later. Before,getStatecould reportReadyfor it. - With the page grid on, the canvas no longer redraws on every frame while nothing changes.
- An image or PDF export now fails at once with an error while a video export runs, and the video export continues. Before, the engine crashed at the next frame of the video export.
- In a pixel scene, a move or resize that an exclusion area or the page movement constraint stops now ends exactly at that edge. Touch gestures, proportional corner resizes and cropping resizes rounded the block up to half a pixel past it.
- In a pixel scene with a movement constraint, a drag or a touch pan that the constraint does not stop now ends on whole pixels.
- Playback no longer stops by itself while video thumbnails load, such as when the user presses Play before the timeline thumbnails are ready. This happened on a page that shows its title.
- Loading a scene or archive now restores the variables of the scene, also when
overrideEditorConfigis false. Before, a loaded scene kept its text tokens and variable bindings but lost the variables that they use. - A straightened image or video has smooth slanted edges on the canvas again. Before, they showed as steps with the
Containcontent fill mode and in crop mode. - Video fills now keep showing their frames in playback and in a video export when memory runs low. Before, the engine released a video that still played, and its fill stayed blank or frozen for seconds while the file loaded again.
- Video exports no longer fail on slow Android devices, such as emulators, when a video clip is slow to decode a frame.
- On Windows, a download whose connection closes before the end of the file now fails, instead of loading part of the file.
- A small block with a drop shadow on a large page now exports to PDF on a device with a texture size limit.
- Pasting text that nests more than 1024 levels, or that has no format or version, now returns an error and no longer ends the process.
- In single page mode, a read of the text lines of a block right after
setCurrentPagesnow measures the page that it shows. - With
features/p3WorkingColorSpaceon, a gradient in a PDF or SVG export keeps its colors instead of looking paler. - Redo of a removed transition no longer leaves the transition on its clip.
- A page title that the template engine cannot parse or render no longer crashes the engine. Its tokens that name a variable still show their values, and the rest shows as written.
- A text block with automatic width now keeps the width of its text when the camera zooms out.
- Fixed a crash when grouping blocks with nested groups before their initial durations resolve.
- Selecting another block while you edit a path now ends path editing, unless that block has a vector path shape or text on a path. Before, the editor could show an error, and the selected block’s shape changed to a path. A page now enters path editing only when the
page/allowShapeChangesetting is on. - On Apple platforms, video export no longer slows down when a clip starts far into its video file.
- Fixed a crash when a WebM video with an audio track was selected or added to the timeline.
Engine/Web
- Fixed videos that stopped playing and exporting after 16 video exports from one engine.
- A video or an archive from a local upload that another tab or writer holds open no longer logs an error on every frame. The engine logs one warning, tries again once a second, and loads the file when it is free.
- A local upload that the browser finds but then cannot open for another reason fails to load with one error. Before, the engine tried to open it again on every frame.
- A PDF export that cannot use temporary storage builds the PDF in memory, as before. It no longer leaves a background worker running until the page closes.
Engine/Android
BlockApi.export,exportWithColorMaskandexportVideono longer crash the app at the start of an export while the editor is open.- Fixed the first frame of a new video clip, which on some devices showed only after 10 seconds or more.
Engine/Swift
- A video export no longer stops for 2 seconds at a time on H.264 and HEVC videos with B-frames that the export starts to decode.
Engine/Node
- An image export now uses a fractional target width and height as given, instead of cutting them to whole numbers.
Engine/Node Native
block.exportVideoandblock.exportAudiono longer throw when an option isundefinedornull. The option keeps its default.
Editor
- Fixed the replace panel staying closed after switching to the Adopter role with a placeholder selected.
Editor/Web
- Fixed “Merge with previous” on captions. It no longer fails on a caption with text, and merging an empty caption no longer repeats the previous caption’s text. After Backspace joins two captions in the Captions panel, the cursor sits where the two captions were joined.
- In Safari, Escape closes an open menu or popover after a click inside it, such as the crop fill mode menu. Before, it stayed open and covered the next control.
- Moving a timeline clip one slot earlier with Mod+ArrowLeft keeps the clips after it in place.
- In the video timeline clip menu, Move Left is now off for the first clip of a track and Move Right for the last. Before, Move Left on the first clip failed with an error and left the menu open. Move Right on the last clip did nothing but add an undo step.
- A video or audio clip played faster or slower keeps its in-point when another clip on the timeline is moved or trimmed. Trimming the start of such a clip on the timeline moves its in-point by the right amount of media.
- The blue tint that marks a transition in the video timeline now draws below the trim handles, icons and selection frame of its clips. Before, it covered them.
- When a checkbox label runs over several lines, the checkbox and its icon line up with the first line. This applies to every checkbox, including
builder.Checkbox, whose row now grows to fit the label. - A slider change now gets its own undo step when the browser cancels the touch drag or the focus leaves the slider during a press.
- Resizing a text block now always shows the notification when its frame behavior changes. Before, it was missing after you selected another text block, or after you changed the frame behavior in the advanced text panel.
- A time that rounds up to a full minute or hour now carries over, so 59.96 seconds reads “1:00.0” instead of “0:60.0”. This shows in the video timeline, the trim inspector and the caption times.
- Rubber-band selection in the video timeline no longer selects clips whose
editor/selectscope is off. engine.editor.getSettingnow returnstimeline/trackVisibility,timeline/transitionControlVisibility,dock/iconSize,dock/hideLabelsandcolorPicker/colorModeinstead of throwing, andsetSettingandgetSettingaccept these settings with theubq://prefix.timeline.zoom.toLevelkeeps the timeline zoom inside its range, and Fit View no longer zooms in past the largest zoom. A level of 0 or below no longer breaks the timeline, andNaNis rejected.- The video timeline stays on screen when a timeline action runs right after
engine.scene.createVideo(), and moving the playhead in that moment no longer throws. - The video timeline no longer throws when a clip is destroyed through the API while it is dragged, hovered or shown for the first time.
- A timeline drop that the editor cannot write, for example because of scopes, now snaps back to where the scene has the clip.
- A video or image applied to a page whose duration track is empty keeps its own duration. Before, it got a duration of 0.
- Fixed an error when loading another scene with the Layers panel open.
Editor/Android
- The
BackgroundRemovalPluginnow keeps a rotated photo, such as a portrait camera photo, upright. Before, the result came out sideways. - A zoom level you set now survives a rotation, a fold or a window resize. Before, the editor fit the page to the new window and the zoom was lost.
- After a rotation, the page fits the new window. Before, it kept the size that fit the old window, so part of it was off screen.
- Auto captions no longer reverts the edits made while it reads the audio of a video.
- Fixed the “Unmute other Audio” label in the voiceover sheet, which was cut off. A long label now wraps to a second line.
- A fast scroll of the timeline to its end now moves the playhead to the end and shows the last frame. Before, the playhead stopped early and jumped on the next scroll.
Editor/iOS
- The
BackgroundRemovalPluginand its loaded model are now freed when nothing else holds the plugin. Before, its own buttons kept it in memory. - The
BackgroundRemovalPluginno longer keeps a closed editor in memory. Before, a running removal held the editor’s engine until it ended. - Fixed the keyboard staying open in the Resize dialog on iOS 26. Opening the Unit, Resolution or Font size unit picker now hides the keyboard, as on earlier iOS versions.
- Auto captions no longer adds an extra undo step for each video that it transcribes.
- Crop preset categories now follow the configured asset source order, keeping the initial category consistent between openings.
- A long button label in the voiceover sheet now wraps to a second line instead of truncating.
- An open transition settings page now updates after an undo or a redo. Before, it kept its rows and its duration limit until it opened again.
Plugins/Web
terminate()onImageEnhancerin@imgly/plugin-perfectlyclear-webnow also stops the inference worker, which kept the AI models loaded. Aninit()that fails now stops the workers it started. Aninit()orenhance()call in progress now rejects when you callterminate(), and the next call starts a new runtime. The error’snameisAbortError.
Renderer
--helpnow reports correctly whether theCESDK_LICENSEenvironment variable is set.- A failed export now releases its license seat right away, instead of holding it until the license server times it out.