<?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>Mirko – IMG.LY Blog</title><description>Posts by Mirko on the IMG.LY blog.</description><link>https://img.ly/blog/author/mirko/</link><language>en-us</language><image><url>https://img.ly/apple-touch-icon.png</url><title>Mirko – IMG.LY Blog</title><link>https://img.ly/blog/author/mirko/</link></image><atom:link href="https://img.ly/blog/author/mirko/rss.xml" rel="self" type="application/rss+xml"/><generator>Astro</generator><lastBuildDate>Fri, 12 Jun 2026 10:10:23 GMT</lastBuildDate><ttl>60</ttl><item><title>Make Your Docs Agent-Ready: Compiling MDX into Markdown</title><link>https://img.ly/blog/making-docs-machine-readable-why-we-native-compile-markdown-for-ai-agents/</link><guid isPermaLink="true">https://img.ly/blog/making-docs-machine-readable-why-we-native-compile-markdown-for-ai-agents/</guid><description>We rebuilt our docs pipeline to serve both HTML for humans and fully resolved Markdown for AI agents: 7× lighter and easier for tools to ingest.</description><pubDate>Wed, 11 Mar 2026 14:38:44 GMT</pubDate><content:encoded>&lt;h2 id=&quot;tldr-the-7x-efficiency-gain&quot;&gt;TL;DR: The 7x Efficiency Gain&lt;/h2&gt;
&lt;p&gt;We rebuilt our documentation pipeline to treat AI agents as a first-class audience. By natively compiling MDX into clean, fully-resolved Markdown—rather than heavy HTML or unresolved source code—agents and LLMs can now ingest our docs &lt;strong&gt;7x faster&lt;/strong&gt; and with far higher accuracy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Test it yourself:&lt;/strong&gt;&lt;br&gt;
Request our docs with the &lt;code&gt;Markdown&lt;/code&gt; accept header to see the agent-optimized view:&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;curl&lt;/span&gt;&lt;span&gt; -s&lt;/span&gt;&lt;span&gt; -H&lt;/span&gt;&lt;span&gt; &quot;Accept: text/markdown&quot;&lt;/span&gt;&lt;span&gt; https://img.ly/docs/cesdk/js/settings-970c98/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;the-documentation-mismatch-humans-vs-agents&quot;&gt;The Documentation Mismatch: Humans vs. Agents&lt;/h2&gt;
&lt;p&gt;If you build developer tools today, you have two distinct audiences: human engineers and the AI agents they use to write code. Recently, we realized we were treating them exactly the same.&lt;/p&gt;
&lt;p&gt;Our docs pipeline is built on MDX and optimized for the browser. But when an LLM tries to ingest a page, it has to wade through navigation chrome, layout wrappers, and raw JSX. We didn’t just need a “clean text” mode; we needed a fundamentally different architecture.&lt;/p&gt;
&lt;h3 id=&quot;humans-vs-agents-a-comparative-overview&quot;&gt;Humans vs. Agents: A Comparative Overview&lt;/h3&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Feature&lt;/th&gt;&lt;th&gt;Humans (Browser)&lt;/th&gt;&lt;th&gt;Agents (Context Window)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Primary Format&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;HTML + CSS + JS&lt;/td&gt;&lt;td&gt;Clean Markdown&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Navigation&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Visual UI (Sidebar/Tabs)&lt;/td&gt;&lt;td&gt;Explicit links + hierarchy&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Context&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Implicit (Site layout)&lt;/td&gt;&lt;td&gt;Explicit (Frontmatter + headers)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Constraints&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Performance / Core Web Vitals&lt;/td&gt;&lt;td&gt;Token / Context Window budgets&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Payload Size&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;~222 KB&lt;/td&gt;&lt;td&gt;&lt;strong&gt;~31 KB (7x smaller)&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;the-problem-why-html-and-raw-mdx-fail-agents&quot;&gt;The Problem: Why HTML and Raw MDX Fail Agents&lt;/h2&gt;
&lt;h3 id=&quot;1-html-is-expensive-chrome&quot;&gt;1. HTML is expensive “Chrome”&lt;/h3&gt;
&lt;p&gt;A documentation site is an application. Even static sites ship navigation chrome, scripts, and styling hooks. For AI, this is “token noise.” You are paying for bytes that provide zero value to the LLM and forcing the agent to reconstruct a hierarchy that you already had at authoring time.&lt;/p&gt;
&lt;h3 id=&quot;2-raw-mdx-is-unresolved-source-code&quot;&gt;2. Raw MDX is unresolved source code&lt;/h3&gt;
&lt;p&gt;Serving raw MDX files doesn’t solve the problem either. MDX is for maintainers—it is full of imports and unresolved dependencies. Our documentation often pulls code from external, tested repositories:&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;## Initialize the Engine&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;CodeBlock file=&quot;examples/getting-started/src/index.ts&quot; lines=&quot;12-24&quot; /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the browser, this renders beautifully. In raw MDX, it’s a pointer to a file the agent cannot see. The actual code isn’t there.&lt;/p&gt;
&lt;h2 id=&quot;the-solution-treat-markdown-as-a-compilation-target&quot;&gt;The Solution: Treat Markdown as a Compilation Target&lt;/h2&gt;
&lt;p&gt;We stopped thinking about this as “exporting text” and started treating it as what it really is: a &lt;strong&gt;second build target&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Old Pipeline:&lt;/strong&gt; &lt;code&gt;MDX (Source) → HTML (Browser View)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;New Pipeline:&lt;/strong&gt; &lt;code&gt;MDX (Source) → HTML&lt;/code&gt; &lt;strong&gt;AND&lt;/strong&gt; &lt;code&gt;MDX (Source) → Markdown (Agent View)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-we-didnt-just-convert-html&quot;&gt;Why we didn’t just “convert” HTML&lt;/h3&gt;
&lt;p&gt;We tried rendering to HTML and then running a converter. It failed at &lt;strong&gt;code blocks&lt;/strong&gt;. Syntax highlighting adds spans and wrappers around tokens; reversing that into clean, trustworthy code is nearly impossible.&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;pre&lt;/span&gt;&lt;span&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;language-js&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;token keyword&quot;&lt;/span&gt;&lt;span&gt;&gt;const&amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;token variable&quot;&lt;/span&gt;&lt;span&gt;&gt;engine&amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;pre&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By working at the AST (Abstract Syntax Tree) level &lt;em&gt;before&lt;/em&gt; rendering, we avoid this entirely.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;implementation-transforming-mdx-at-the-ast-level&quot;&gt;Implementation: Transforming MDX at the AST Level&lt;/h2&gt;
&lt;p&gt;Rather than trying to reconstruct meaning from rendered markup, we preserve it directly. We built a &lt;code&gt;remark&lt;/code&gt; plugin that transforms MDX at the MDAST level.&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; { remark } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;remark&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; remarkMdx &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;remark-mdx&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; remarkStringify &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;remark-stringify&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; { remarkTransformForExport } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./remarkTransformForExport&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; processor&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; remark&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;use&lt;/span&gt;&lt;span&gt;(remarkMdx) &lt;/span&gt;&lt;span&gt;// Parse MDX/JSX nodes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  .&lt;/span&gt;&lt;span&gt;use&lt;/span&gt;&lt;span&gt;(remarkTransformForExport, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    baseUrl: &lt;/span&gt;&lt;span&gt;&apos;[https://img.ly/docs/cesdk/](https://img.ly/docs/cesdk/)&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    paths: resolvedPathMap,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;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;use&lt;/span&gt;&lt;span&gt;(remarkStringify); &lt;/span&gt;&lt;span&gt;// Back to markdown&lt;/span&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; markdown&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; processor.&lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;span&gt;(mdxContent);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-component-contract&quot;&gt;The Component Contract&lt;/h3&gt;
&lt;p&gt;Every MDX component must define how it exports to the agent view. We colocate the transform directly with the component: &lt;code&gt;Aside.astro&lt;/code&gt; (Human UI) ↔ &lt;code&gt;Aside.toMarkdown.ts&lt;/code&gt; (Agent logic).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example: Aside Component → Blockquote&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Input:&lt;/strong&gt; &lt;code&gt;&amp;#x3C;Aside title=&quot;Pro Tip&quot;&gt;Use the basePath...&amp;#x3C;/Aside&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;&gt; **Pro Tip:** Use the basePath...&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example: Resolving Code References&lt;/strong&gt; This is the most critical transform. Instead of a file pointer, the agent gets the actual, inlined code block fetched during the build process.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;translating-navigation-ui-into-text&quot;&gt;Translating Navigation UI into Text&lt;/h2&gt;
&lt;p&gt;When you strip the sidebar and footer, you lose context. We reintroduce “navigational chrome” as text-native primitives at the top and bottom of every Markdown file:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;markdown&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;Working with Filters&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;platform&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;react&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;[https://docs.example.com/react/guides/filters/](https://docs.example.com/react/guides/filters/)&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;&gt; You’re reading the React docs. For the full corpus, see llms-full.txt.&lt;/span&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;**Path:**&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;Home&lt;/span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt;https://docs.example.com/&lt;/span&gt;&lt;span&gt;) &gt; [&lt;/span&gt;&lt;span&gt;Guides&lt;/span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt;https://docs.example.com/guides/&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-contained Page Content]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;## Continue Reading&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;Color Adjustments&lt;/span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt;https://docs.example.com/react/guides/color-adjustments/&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;API Reference&lt;/span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt;https://docs.example.com/api/&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;lessons-for-scaling-agentic-dx&quot;&gt;Lessons for Scaling Agentic DX&lt;/h2&gt;
&lt;p&gt;If you maintain docs at scale, “optimizing for bots” is no longer optional—it is the new standard for Developer Experience (DX).&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Don’t serve HTML and hope:&lt;/strong&gt; Reverse-engineering structure is hard for AI. Give it the structure directly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Every component needs an identity:&lt;/strong&gt; If a component carries meaning, it needs a Markdown equivalent. If it’s just layout, unwrap it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resolve everything:&lt;/strong&gt; Assume zero ambient context. Links must be absolute, and code must be inlined.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content Negotiation:&lt;/strong&gt; Use the &lt;code&gt;Accept: text/markdown&lt;/code&gt; header. It is becoming the industry standard for tools like Claude Code and OpenCode.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Single-File Corpus:&lt;/strong&gt; In addition to per-page files, generate a &lt;code&gt;llms-full.txt&lt;/code&gt; that concatenates everything. Some agents prefer one large fetch over a crawl.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By acknowledging that AI agents are a primary consumer of our documentation, we’ve made our SDK significantly easier to integrate. The future of docs isn’t just “readable”. It’s “storable and traversable.”&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Note: We built this for CE.SDK. The implementation uses Astro and Vercel, but the approach is framework-agnostic.&lt;/em&gt;&lt;/p&gt;</content:encoded><dc:creator>Mirko</dc:creator><media:content url="https://blog.img.ly/2026/03/optimize-docs-for-agents-ai-llm-1.jpg" medium="image"/></item><item><title>IMG.LY Research: AI-based Generative Design Editing</title><link>https://img.ly/blog/img-ly-research-ai-based-generative-editing/</link><guid isPermaLink="true">https://img.ly/blog/img-ly-research-ai-based-generative-editing/</guid><description>Combine Large Language Models (LLMs) with CE.SDK for design edits and automated creative workflows.</description><pubDate>Tue, 16 Jul 2024 10:44:05 GMT</pubDate><content:encoded>&lt;p&gt;Generative AI is transforming the tech landscape, finding applications in virtually every field. At &lt;a href=&quot;https://img.ly/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=generative-editing&quot;&gt;IMG.LY&lt;/a&gt;, we’re exploring how these advancements can revolutionize creative workflows. This article presents a research project, where we integrate Large Language Models (LLMs) with our flagship product, CreativeEditor SDK (CE.SDK), to enable natural language-driven design edits.&lt;/p&gt;
&lt;p&gt;Our flagship product, CreativeEditor SDK (CE.SDK) allows for advanced creative workflows for countless use cases in industries ranging from &lt;a href=&quot;https://img.ly/industries/print&quot;&gt;print&lt;/a&gt; to &lt;a href=&quot;https://img.ly/industries/marketing-tech&quot;&gt;marketing tech&lt;/a&gt;. Most use cases can be realized with the out-of-the-box feature set, but it also exposes a best-in-class API, called Engine API, to build complex custom workflows with designs and videos.&lt;/p&gt;
&lt;p&gt;In this article, we will showcase how to combine our CE.SDK Engine API with LLMs to edit designs with natural language.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/imgly-research/imgly-ai-template-editor-demo.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h2 id=&quot;introduction-to-llms&quot;&gt;Introduction to LLMs&lt;/h2&gt;
&lt;p&gt;Generative AI is often associated with chatbots, but its capabilities stretch further. It is a versatile text processor that can transform any textual input into various structured outputs. This adaptability is due to its training on diverse textual patterns, allowing it to support a wide range of text-to-text applications beyond just generating conversational prose.&lt;/p&gt;
&lt;p&gt;Carefully crafting an input text (prompt) in a way that instructs the LLM to output a specific structured output format allows us to use LLMs to solve almost any arbitrary text-based task.&lt;/p&gt;
&lt;p&gt;Crafting prompts is an art in itself. When ensuring that we receive the required output we need to adhere to the following steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Consider what type of data our model was trained on to ensure the correct formatting of our input text. The most basic example is using English as our “main prompt language” since most LLMs are mainly trained on English text samples.&lt;/li&gt;
&lt;li&gt;All necessary problem-specific information to solve the task needs to be included in the prompt. While LLMs often possess an inherent understanding of the world inferred from the vast amount of text they are trained on, they may not know much about our specific problem. Furthermore, LLMs have the notorious tendency to hallucinate, that is fill in missing context with incoherent or incorrect information. To ensure the best performance, the input to the LLM must provide as much context as possible.&lt;/li&gt;
&lt;li&gt;Finally, we need to instruct the LLM well enough to output text-based data in a format we can then parse and process.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;human-vs-ai-workflows-for-executing-design-tasks&quot;&gt;Human vs. AI Workflows for Executing Design Tasks&lt;/h2&gt;
&lt;p&gt;We started this project with the vision to use generative AI to magically handle requests like these (in increasing order of complexity):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Make the logo bigger&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Translate this design into German&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Adopt this template to our brand colors and brand assets&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Transform this Instagram story portrait design into a landscape YouTube thumbnail&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When trying to delegate a task to an AI, it’s best to start by thinking about how these tasks are currently solved by humans.&lt;/p&gt;
&lt;p&gt;Let’s walk through how a human would complete a task such as&lt;br&gt;
&lt;code&gt;Make the logo bigger&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Humans would visually scan the design and automatically segment it by its elements such as objects, backgrounds, or text.&lt;/li&gt;
&lt;li&gt;Humans would then read and comprehend the task “Make the logo bigger”&lt;/li&gt;
&lt;li&gt;Finally, users would use their existing knowledge of how to move and interact with design software to fulfill the task by manipulating the individual elements in the design.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Based on these considerations we can extract the implicit knowledge necessary to fulfill a task and make it explicit for the benefit of our LLM.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Design Representation:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To enable the LLM to understand and manipulate a design, it is essential to provide a representation of the design. This can be either textual or a mix of textual and visual (if the LLM has vision capabilities) data. While supplying the current design as a raster image to the LLM is trivial, serializing a CE.SDK design into a textual format requires a custom serialization process. The textual representation is important since it allows the LLM to identify, address, and comprehend the different components of the design effectively.&lt;/p&gt;
&lt;p&gt;Refer to &lt;code&gt;Appendix: Use-Case dependent serialization of CE.SDK Designs&lt;/code&gt; for a more in-depth explanation of how to accomplish this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Editing Protocol:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;LLMs do not interact with design software using traditional human interfaces like a mouse, keyboard, or visual feedback. Therefore, we need a specific protocol for the LLM to propose changes to the design. We have developed a method where we pass a textual representation of the design to the LLM as part of the prompt so that the LLM can indicate changes to the design by returning a modification of this representation.&lt;/p&gt;
&lt;p&gt;Practically, this means that if we pass in an element such as &lt;code&gt;&amp;#x3C;Image id=&quot;1337&quot; x=”100” y=&quot;100&quot; .../&gt;&lt;/code&gt;, the LLM can change those x and y attributes by simply returning &lt;code&gt;&amp;#x3C;Image id=&quot;1337&quot; x=&quot;0&quot; y=&quot;0&quot; .. /&gt;&lt;/code&gt; inside its output text. Since we can identify the design element that was changed using the ID attribute, we can then calculate the programmatic changes that need to be applied to the design, like in this case &lt;code&gt;engine.block.setPositionX(1337, 0)&lt;/code&gt; and &lt;code&gt;engine.block.setPositionY(1337, 0)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Refer to &lt;code&gt;Appendix: Parsing and transforming LLM response&lt;/code&gt; for a deeper look into this topic.&lt;/p&gt;
&lt;h2 id=&quot;using-generative-ai-to-execute-design-related-tasks&quot;&gt;Using Generative AI to Execute Design-Related Tasks&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;Employ LLMs to modify and improve CE.SDK design templates.&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;879&quot; src=&quot;https://img.ly/_astro/ai-generated-design-templates-LLM_1BWRFe.webp&quot; srcset=&quot;/_astro/ai-generated-design-templates-LLM_1etJt1.webp 640w, /_astro/ai-generated-design-templates-LLM_Z2amSOe.webp 750w, /_astro/ai-generated-design-templates-LLM_2jWNYV.webp 828w, /_astro/ai-generated-design-templates-LLM_Lp5Ga.webp 1080w, /_astro/ai-generated-design-templates-LLM_2qLpKx.webp 1280w, /_astro/ai-generated-design-templates-LLM_Z1OfXUH.webp 1668w, /_astro/ai-generated-design-templates-LLM_1BWRFe.webp 2000w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Based on what we have learned, we assemble a workflow, with the LLM as the center point that allows the LLM to execute design-related tasks on any of our CE.SDK designs. This workflow can be divided into two sub-tasks: Composing an input text (prompt) with all necessary context and parsing and applying the output of the LLM to the CE.SDK design.&lt;/p&gt;
&lt;h3 id=&quot;composing-the-input-text&quot;&gt;Composing the Input Text&lt;/h3&gt;
&lt;p&gt;As seen in the graphic above we first compose the input text based on different components to provide the model with all necessary context to fulfill the user’s editing request: This includes a general text to “instruct” the model, the actual request that the user entered, an exemplary design representation to explain our output format to the model and a representation of the currently edited design.&lt;/p&gt;
&lt;p&gt;The general, static text to “instruct” the model is composed of the following three parts.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What we are trying to achieve in general:&lt;br&gt;
&lt;code&gt;&quot;You are an AI with expertise in design, specifically focused on XML representations of designs&quot;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Output format instructions:&lt;br&gt;
&lt;em&gt;Your responses should only contain one XML document. Ensure that you do not introduce new attributes to any XML elements. You can change image elements by setting the alt attribute, which then will be used to search Unsplash for a fitting image. A sample alt text is “A mechanic changing tires with a pair of beautiful work gloves on”.&lt;br&gt;
Additionally, pay close attention to the layout: verify that no elements in the XML document extend beyond the page boundaries. This constraint is critical for maintaining consistency and accuracy in XML formatting. Always double-check your XML output for these requirements.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;The actual user request:&lt;br&gt;
e.g., &lt;code&gt;&quot;Make the logo bigger&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By including a textual representation of a comprehensive example design we can show the model which layer types are available as well as which properties of those layers can be manipulated.&lt;/p&gt;
&lt;p&gt;The model now has all the necessary context and building blocks to respond to user requests in a format that we can process downstream.&lt;/p&gt;
&lt;h3 id=&quot;applying-the-output-text&quot;&gt;Applying the Output Text&lt;/h3&gt;
&lt;p&gt;In the first step, we scan the output text for an XML-like document and if we find one, attempt to parse it.&lt;/p&gt;
&lt;p&gt;This will yield a structured data object we can compare to the one we passed in and calculate which elements have been modified, added, or removed.&lt;/p&gt;
&lt;p&gt;The resulting change set can then be translated into specific calls to the &lt;a href=&quot;https://img.ly/docs/cesdk/js/user-interface/ui-extensions-d194d1/&quot;&gt;CE.SDK Engine API&lt;/a&gt; to change the current design.&lt;/p&gt;
&lt;h2 id=&quot;issues-faced&quot;&gt;Issues faced&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Latency&lt;/strong&gt;: One issue with state-of-the-art models is their big latency: Each LLM response contains approximately 1000 tokens. That means each request takes 7-45 seconds (depending on the model) to complete. This long delay may be unacceptable for some user experiences. However, we see this issue as transitory and expect upcoming models to have much smaller latency while maintaining their capabilities.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt;: Each request/response with GPT-4 turbo as a backing model costs around 5 cents and restricts some use cases. We also expect the pricing to drop significantly. The new GPT-4o model for example reduces the price by half.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hallucinations&lt;/strong&gt;: LLMs do not always follow the instructions properly and, e.g., produce output that is not parsable. Hallucinations directly correlate with the capabilities of the model and this issue is not apparent at current state-of-the-art LLMs like e.g GPT-4/GPT-4o.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;We present a novel and adaptable approach to use Generative AI and LLMs specifically to interact with IMG.LY’s CreativeEditor SDK. We showcase how this technology can be used to execute common design requests on arbitrary CE.SDK Designs. The proposition that LLMs can understand textual representations of visual elements was by no means obvious. This research project has revealed that it is very well within the scope of LLMs to translate instructions from a visual semantic context to its textual representation and back. This invites more inquiries into LLMs as assistants for tasks with a heavy visual component such as design.&lt;/p&gt;
&lt;p&gt;While further research is needed to make this technology available in production environments, we are confident that Generative AI-based editing will play a big role in the future of Graphics and Video editing.&lt;/p&gt;
&lt;h2 id=&quot;appendix-use-case-dependent-serialization-of-cesdk-designs&quot;&gt;Appendix: Use-case Dependent Serialization of CE.SDK Designs&lt;/h2&gt;
&lt;p&gt;LLMs work based on “tokens” which are equal to words. However, a design, like e.g a poster design or a social media graphic, is highly visual. That means that we need a way to convert a design into text, a way to serialize it. Our CE.SDK engine can serialize an existing scene using our &lt;code&gt;engine.block.saveToString&lt;/code&gt; method. However, this serialization contains a huge pile of information that is not necessary to do edits inside the file. LLMs are priced by token and their speed is also relative to the number of tokens the input and output have. Thus, the number of tokens should be reduced.&lt;/p&gt;
&lt;p&gt;We looked at several ways to convert the current state of the design into a textual representation. Since GenAI is trained on a lot of (X)HTML which has an XML-like format, we decided to serialize any designs into a tag-based XML-like format.&lt;/p&gt;
&lt;p&gt;The IMG.LY editor internally refers to design elements like images or texts as “blocks”. These blocks are uniquely identifiable and addressable using a numeric ID. We use this ID to be able to identify a serialized design block in the input and output of the LLM. Example: &lt;code&gt;&amp;#x3C;Image id=&quot;12582927&quot; x=&quot;0&quot; y=&quot;0&quot; width=&quot;800&quot; height=&quot;399&quot; /&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;For each of the CE.SDK block types like e.g “Text” or “Graphics” (representing images or vector shapes), we will the CE.SDK Engine API to query very specific data from the block. That means that for example we only have a text attribute for Text blocks.&lt;/p&gt;
&lt;p&gt;This rather specific mapping of only certain properties from the CE.SDK design into the text serialization allows us to optimize the design serialization for different use cases. A use-case where we e.g. want to automatically name each layer does maybe not require fine-grained information about e.g the font size.&lt;/p&gt;
&lt;h2 id=&quot;appendix-parsing-and-transforming-llm-response&quot;&gt;Appendix: Parsing and transforming LLM response&lt;/h2&gt;
&lt;p&gt;The LLM answers with arbitrary tokens. It’s not possible to restrict the response to a certain syntax. By settling on a well-defined and widely used format we instruct the model to also reply with an XML-like document, similar to the one we passed in as “current state”.&lt;/p&gt;
&lt;p&gt;After receiving the LLM’s Response we first make sure that only a single XML document is present inside the response. We then compare the retrieved XML document with the state of the Design that we passed into the LLM and generate a change set. This change set contains entries like “Color of block with ID=123 has changed”. These change set entries are then converted into programmatic commands, like e.g &lt;code&gt;engine.block.setColor(123)&lt;/code&gt; and executed on the current design.&lt;/p&gt;
&lt;p&gt;One challenges when working with an LLM is the inability to restrict the output space. Thus, we are never guaranteed that the LLM did not add e.g new XML node names or that it even replies with a proper, valid XML-like document. The only lever to influence the probability of a proper XML-like document is to use strong prompting and LLM that are good at following those instructions.&lt;/p&gt;
&lt;p&gt;In our tests, state-of-the-art models like GPT-4 can follow those instructions without any further tooling.&lt;/p&gt;
&lt;h2 id=&quot;further-research-topics&quot;&gt;Further Research Topics&lt;/h2&gt;
&lt;p&gt;It’s also worth exploring fine-tuning an LLM specifically for this task which could improve the performance of the LLM for the specific tasks.&lt;/p&gt;
&lt;p&gt;It would also be possible to use more advanced libraries like &lt;a href=&quot;https://github.com/guidance-ai/guidance&quot;&gt;Guidance&lt;/a&gt;, which allows to define a grammar for the LLM response thus making sure that the output of the LLM is always parseable.&lt;/p&gt;
&lt;p&gt;Another way to improve the performance would be to methodically test different prompt templates and find a way to measure and compare the output quality.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thank you for reading!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3,000+ creative professionals gain exclusive access and hear of our releases first—&lt;/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 and never miss out.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Mirko</dc:creator><media:content url="https://blog.img.ly/2024/07/0_AI-Template-generator.jpg" medium="image"/><category>AI</category><category>Automation</category><category>Design Editor</category><category>Machine Learning</category></item></channel></rss>