<?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>Dustin – IMG.LY Blog</title><description>Dustin contributes to the design of nearly everything you see from IMG.LY: the CE.SDK editor and the website, where he&apos;s the mastermind behind the look and feel and just as stubborn about the UX underneath it. He&apos;s also quietly the most organized person in the building, a Notion wizard who keeps ending up leading initiatives he only meant to help with. And he builds his own tools: when the existing Figma plugins couldn&apos;t handle CE.SDK&apos;s custom iOS icons, he wrote SF Symbol Generator and shipped it to the Figma Community. Away from the screen: table tennis, a balcony garden, and an ongoing project to perfect the air-fryer cheesecake, which is either engineering or heresy depending on who you ask.</description><link>https://img.ly/blog/author/dustin/</link><language>en-us</language><image><url>https://img.ly/apple-touch-icon.png</url><title>Dustin – IMG.LY Blog</title><link>https://img.ly/blog/author/dustin/</link></image><atom:link href="https://img.ly/blog/author/dustin/rss.xml" rel="self" type="application/rss+xml"/><generator>Astro</generator><lastBuildDate>Tue, 01 Sep 2026 09:42:36 GMT</lastBuildDate><ttl>60</ttl><item><title>Evaluating CoDesign MCP performance in resizing</title><link>https://img.ly/blog/evaluating-codesign-mcp-performance-in-resizing/</link><guid isPermaLink="true">https://img.ly/blog/evaluating-codesign-mcp-performance-in-resizing/</guid><description>One approved design, five ad formats, six AI models, the same brief. All six came back with ads you could ship, and Gemini 3.7 Flash did it for thirty cents. Here is what our automated checks can prove about that work, and what they cannot.</description><pubDate>Fri, 21 Aug 2026 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You designed a banner. The same day, you need it in five sizes, and none of them can be wrong.&lt;/p&gt;
&lt;p&gt;We handed that job to six AI models through &lt;a href=&quot;https://img.ly/codesign/&quot;&gt;CoDesign&lt;/a&gt; MCP: take this approved design and rebuild it for five ad formats. All six came back with ads you could ship. Gemini 3.7 Flash did it for thirty cents and passed every check we wrote.&lt;/p&gt;
&lt;p&gt;That is what CoDesign MCP is for. You point an AI assistant such as Claude at your approved design, name the formats you need, and it opens the file, rebuilds the layout for each one and exports production files. What comes back is an editable design file, not a picture of one.&lt;/p&gt;
&lt;p&gt;Speed and price are the easy things to measure. We wanted to measure quality too, so we built an eval that runs the job automatically, opens every result and reports what passed. This is the first scenario out of that suite.&lt;/p&gt;
&lt;h2 id=&quot;three-banners-one-brief&quot;&gt;Three banners, one brief&lt;/h2&gt;
&lt;p&gt;Three different models were handed the same 320×50 slot, cut from the same master creative against the same brief, and all three came back with an ad you could traffic tomorrow. Every one of them keeps the wordmark and the button, nothing is clipped, and no type is set below 11px.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Three 320×50 mobile banners built from the same ATLAS master by three different models&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1200px) 1200px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1200&quot; height=&quot;207&quot; src=&quot;https://img.ly/_astro/hook-three-mobile-strips.Q8QXs3k1_2XYzx.webp&quot; srcset=&quot;/_astro/hook-three-mobile-strips.Q8QXs3k1_Z1pTCV1.webp 640w, /_astro/hook-three-mobile-strips.Q8QXs3k1_2oKvSC.webp 750w, /_astro/hook-three-mobile-strips.Q8QXs3k1_27uKLv.webp 828w, /_astro/hook-three-mobile-strips.Q8QXs3k1_ZNKOe4.webp 1080w, /_astro/hook-three-mobile-strips.Q8QXs3k1_2XYzx.webp 1200w&quot;&gt;&lt;/p&gt;
&lt;p&gt;The 300×250 they came from carried five things: the wordmark, the headline, a retention chart, the offer line and the button. At 320×50 the wordmark and the button survive and the chart cannot, which leaves room for exactly one line of copy, so each model had to choose between the headline and the offer.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One dropped the headline and kept the offer, “14-day free trial”.&lt;/li&gt;
&lt;li&gt;One kept the headline but cut “your users” down to “users” to buy itself the line.&lt;/li&gt;
&lt;li&gt;One kept the headline whole across two lines and dropped the offer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We have opinions about which of those reads best, the way any design team would. What we do not have is a way to prove one of them correct, because the choice depends on whether this campaign is selling the product or the trial, and that lives in the media brief rather than in the design. Our checks can confirm that all three are valid and that none of them are broken; they cannot tell you which one the campaign actually needed. Grading the half that has a right answer and leaving the other half to the people who own the brief is the distinction our evals are built around.&lt;/p&gt;
&lt;h2 id=&quot;what-is-gradeable-and-what-is-not&quot;&gt;What is gradeable and what is not&lt;/h2&gt;
&lt;p&gt;The obvious thing to evaluate is the thing that demos well, prompt in and poster out, but we were more interested in the work that actually fills a designer’s week, which is mostly derivative. The design decisions were made once, at the master, and everything after that is a matter of carrying them faithfully into thirty other shapes. That is more technical work than creative work, which is exactly why it is worth handing to an agent.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Three valid 300×600 versions of the same ATLAS banner, differing in type size and spacing&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1200px) 1200px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1200&quot; height=&quot;702&quot; src=&quot;https://img.ly/_astro/gradeable-three-halfpage-variants.D3Bbuya5_2lpIzm.webp&quot; srcset=&quot;/_astro/gradeable-three-halfpage-variants.D3Bbuya5_Z1FdAzV.webp 640w, /_astro/gradeable-three-halfpage-variants.D3Bbuya5_Z1ihu0z.webp 750w, /_astro/gradeable-three-halfpage-variants.D3Bbuya5_FqKnt.webp 828w, /_astro/gradeable-three-halfpage-variants.D3Bbuya5_ZWQJb7.webp 1080w, /_astro/gradeable-three-halfpage-variants.D3Bbuya5_2lpIzm.webp 1200w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Ask which of the finished designs is better and you have a question with no ground truth to appeal to, since human raters disagree with each other and LLM judges reliably prefer their own output. You could ask a lot of humans and rank the results, but that is not something you can run on every build. Wrong designs, though, are wrong in ways you can query: text sitting behind another block, content off the page, type under the legibility floor. Part of design quality is judgement and part of it is fact, and what we have built grades the second part, which means everything below is an automated result rather than a design review. A model can pass every check we wrote and still make an ugly ad.&lt;/p&gt;
&lt;h2 id=&quot;what-we-are-doing-right-now&quot;&gt;What we are doing right now&lt;/h2&gt;
&lt;p&gt;When a model finishes a scenario, programmatic checks open the result and ask whether it is plausibly good. That starts with whether the run produced every design artifact and .imgly file at the right sizes, and the rest depends on the scenario: in a localization run we also check that the words were actually replaced.&lt;/p&gt;
&lt;p&gt;This is a technical floor rather than a grade. LLM judges are still unreliable enough that we do not use one on output quality, because if it is hard for us as designers to say which design is more correct, it is harder for a model, and the result would be noise.&lt;/p&gt;
&lt;p&gt;In the future we plan to experiment more with such methodologies but we also do not want to rely on the false security of an extremely noisy grading process.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;The eval suite&amp;#39;s comparison view for the display-banner matrix&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2396px) 2396px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2396&quot; height=&quot;1344&quot; src=&quot;https://img.ly/_astro/evals-suite-comparison-resize-matrix.CV8e1T_u_8PXp1.webp&quot; srcset=&quot;/_astro/evals-suite-comparison-resize-matrix.CV8e1T_u_Znaqv1.webp 640w, /_astro/evals-suite-comparison-resize-matrix.CV8e1T_u_Z20zsc2.webp 750w, /_astro/evals-suite-comparison-resize-matrix.CV8e1T_u_Z26h51n.webp 828w, /_astro/evals-suite-comparison-resize-matrix.CV8e1T_u_Z1Eg8Vb.webp 1080w, /_astro/evals-suite-comparison-resize-matrix.CV8e1T_u_CxK9F.webp 1280w, /_astro/evals-suite-comparison-resize-matrix.CV8e1T_u_Z1lqFrF.webp 1668w, /_astro/evals-suite-comparison-resize-matrix.CV8e1T_u_ZgDr5i.webp 2048w, /_astro/evals-suite-comparison-resize-matrix.CV8e1T_u_8PXp1.webp 2396w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The comparison view for the display-banner matrix: cost and time sitting directly above what each model actually produced. The 1/1 counts runs, one repeat per cell, not check scores.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;one-master-five-ad-sizes&quot;&gt;One master, five ad sizes&lt;/h2&gt;
&lt;p&gt;The scenario is a display campaign, because that is the version of this problem customers actually have: the same size matrix, every campaign. The master is a 300×250 MPU for ATLAS, an imaginary product-analytics SaaS running a free-trial campaign, carrying a wordmark, the headline “See what your users &lt;strong&gt;actually&lt;/strong&gt; do”, a retention bar chart labelled “43% retained”, the line “14-day free trial. No card.”, and a “Start free trial” button.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;The ATLAS 300×250 master at the top, with an arrow fanning down to the same ad rebuilt at 300×600, 728×90, 320×50 and 160×600&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1200px) 1200px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1200&quot; height=&quot;675&quot; src=&quot;https://img.ly/_astro/master-and-five-sizes.CzF5wnY5_21RxX1.webp&quot; srcset=&quot;/_astro/master-and-five-sizes.CzF5wnY5_FUIkG.webp 640w, /_astro/master-and-five-sizes.CzF5wnY5_Z7tPBU.webp 750w, /_astro/master-and-five-sizes.CzF5wnY5_mzCbS.webp 828w, /_astro/master-and-five-sizes.CzF5wnY5_Zpv1pN.webp 1080w, /_astro/master-and-five-sizes.CzF5wnY5_21RxX1.webp 1200w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The 300×250 master at the top, and the four other sizes on the media plan cut from it. ATLAS is our own fixture, not a real customer.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The brief asks for the full matrix: 300×250, 728×90, 160×600, 300×600 and 320×50. Those sizes span a 16:1 swing in aspect ratio, so the right answer at each one is a genuinely different composition, and at some of them it means taking content out rather than shrinking it.&lt;/p&gt;
&lt;p&gt;Ten checks grade the result and two of them do the real work. &lt;strong&gt;&lt;code&gt;728×90: the chart was dropped, not shrunk&lt;/code&gt;&lt;/strong&gt; kills the model that squashes a bar chart into a 90-pixel strip, which satisfies every geometric constraint in the brief while being completely wrong. &lt;strong&gt;&lt;code&gt;the five sizes are different layouts&lt;/code&gt;&lt;/strong&gt; kills the model that scales one composition five ways and calls it a matrix.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Contact sheet of six 728×90 leaderboards, master first&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1280px) 1280px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1280&quot; height=&quot;676&quot; src=&quot;https://img.ly/_astro/sheet-display-leaderboard.BStENqrh_1X5bgP.webp&quot; srcset=&quot;/_astro/sheet-display-leaderboard.BStENqrh_2uIgrs.webp 640w, /_astro/sheet-display-leaderboard.BStENqrh_Z1sXCsn.webp 750w, /_astro/sheet-display-leaderboard.BStENqrh_Z1JQH3t.webp 828w, /_astro/sheet-display-leaderboard.BStENqrh_Z2gFqpY.webp 1080w, /_astro/sheet-display-leaderboard.BStENqrh_1X5bgP.webp 1280w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The master first, then the six 728×90 leaderboards. Every model that delivered deleted the retention chart.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Every model that delivered arrived at the same composition, wordmark left, headline across the middle, offer underneath it, button on the right and the retention chart deleted, which is six models from five different vendors reaching the same answer independently. We did not expect that, and it is still the result we find hardest to stop thinking about.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Contact sheet of six 160×600 skyscrapers, master first&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1280px) 1280px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1280&quot; height=&quot;676&quot; src=&quot;https://img.ly/_astro/sheet-display-skyscraper.C88KXRcA_Z1lHK5K.webp&quot; srcset=&quot;/_astro/sheet-display-skyscraper.C88KXRcA_Z2cPTfD.webp 640w, /_astro/sheet-display-skyscraper.C88KXRcA_1krJOz.webp 750w, /_astro/sheet-display-skyscraper.C88KXRcA_ZmDw0F.webp 828w, /_astro/sheet-display-skyscraper.C88KXRcA_Z1S3Ozm.webp 1080w, /_astro/sheet-display-skyscraper.C88KXRcA_Z1lHK5K.webp 1280w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The same six at 160×600, master first. With vertical room they all kept the chart. The flat format is where the decision to drop it had to be made.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;which-model-is-best-for-using-with-codesign-mcp&quot;&gt;Which model is best for using with CoDesign MCP?&lt;/h2&gt;





























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;#&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;model&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;checks&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;model spend&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;time&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;tool calls&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;1&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;gemini-3.7-flash&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;10 / 10&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;$0.30&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;6.7 min&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;37&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;2&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;glm-5.2&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;10 / 10&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;$0.40&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;8.2 min&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;25&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;3&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;gpt-5.6-sol&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;10 / 10&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;$0.52&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;4.0 min&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;35&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;4&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;grok-4.6&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;10 / 10&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;$1.20&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;20.9 min&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;39&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;5&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;claude-fable-5&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;10 / 10&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;$3.95&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;10.3 min&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;26&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;6&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;claude-opus-5&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;10 / 10&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;$3.97&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;18.8 min&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;40&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Running this scenario illustrates general numbers across our eval suite. The 6 models above are consistently able to follow the prompts to produce different output designs as requested in the scenario. These numbers also highlight quite the spread in model costs and time spend. What you do not see in these numbers is that glm 5.2, gpt-5.6-sol and grok 4.6 are producing suboptimal designs in our opinion. They are valid, but subjectively fable, opus and gemini 3.7 consistently across runs and across scenarios perform simply better.&lt;/p&gt;
&lt;p&gt;The biggest surprise for us is &lt;strong&gt;Gemini 3.7 Flash&lt;/strong&gt;, which produced the whole five-size matrix for $0.30 in model spend, in under seven minutes. At thirty cents a designer opens six composed variants instead of a blank artboard and takes the best one further, which changes what the tool is for.&lt;/p&gt;
&lt;p&gt;Meanwhile there were no surprises regarding &lt;strong&gt;Opus 5.0 and Fable 5.0&lt;/strong&gt;, both models perform really well - but are slow and expensive.&lt;/p&gt;
&lt;h2 id=&quot;what-does-it-mean-for-codesign-mcp&quot;&gt;What does it mean for CoDesign MCP&lt;/h2&gt;
&lt;p&gt;You can now resize a master design into 5 different sizes reliably for $0.30 using Gemini 3.7 Flash and the CoDesign MCP.&lt;/p&gt;
&lt;p&gt;If you are on a Claude Subscription plan, you might just want to continue using your Claude Code with the CoDesign MCP since it offers the best overall design quality - even if it’s slow and somewhat expensive.&lt;/p&gt;
&lt;p&gt;We will continue to work on upgrading and improving measurements of how well CoDesign MCP works with different models, looking into other scenarios like localization (translating and changing a design for a different locality and language) as well as rebranding existing design assets.&lt;/p&gt;
&lt;p&gt;You can install CoDesign MCP at &lt;a href=&quot;https://img.ly/codesign/&quot;&gt;&lt;strong&gt;img.ly/codesign&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Dustin</dc:creator><dc:creator>Mirko</dc:creator><media:content url="/_astro/key-visual-resize-fanout.qeZDEyVs.png" medium="image"/><category>AI</category><category>Insights</category><category>CE.SDK</category></item><item><title>Building Custom Variable SF Symbols in Figma for our CreativeEditor SDK for iOS</title><link>https://img.ly/blog/building-custom-variable-sf-symbols-in-figma-for-our-creativeeditor-sdk-for-ios/</link><guid isPermaLink="true">https://img.ly/blog/building-custom-variable-sf-symbols-in-figma-for-our-creativeeditor-sdk-for-ios/</guid><description>How we went from community Figma plugins to our own “SF Symbol Generator” to create custom SF Symbols for our CreativeEditor SDK (CE.SDK) for iOS.</description><pubDate>Tue, 13 May 2025 07:00:06 GMT</pubDate><content:encoded>&lt;p&gt;If you’ve ever wrestled with vector paths in Figma just to get a custom variable SF Symbol out the door, you know the frustration: one missed node here, one stray smoothing there, and suddenly your interpolation breaks. Plus, you would design your icons within an SVG file you exported beforehand from the SF Symbols App.&lt;/p&gt;
&lt;p&gt;When Apple introduced &lt;a href=&quot;https://developer.apple.com/sf-symbols/&quot;&gt;SF Symbols&lt;/a&gt; in 2019, they changed the game for iOS design. Suddenly, we had access to thousands of scalable, weight-variable icons that integrate seamlessly with system type. Our &lt;a href=&quot;https://img.ly/products/creative-sdk/&quot;&gt;CreativeEditor SDK&lt;/a&gt; (CE.SDK) is a customizable design and video editor you can integrate into your product. Yet, as it grew, we needed icons as unique as its use cases, icons that simply didn’t exist in Apple’s library.&lt;/p&gt;
&lt;p&gt;In this article, I’ll share our journey from relying on two community Figma plugins (SF Symbols Organizer and Vector Path Editor) to building our own tool, &lt;a href=&quot;https://www.figma.com/community/plugin/1487107089082698461/sf-symbol-generator&quot;&gt;&lt;strong&gt;SF Symbol Generator&lt;/strong&gt;&lt;/a&gt;. Along the way, I’ll show you how we wrestled with aspect ratios, node counts, and path order, and how we eventually streamlined the entire process into a few simple clicks.&lt;/p&gt;
&lt;h2 id=&quot;what-are-variable-sf-symbols&quot;&gt;What Are Variable SF Symbols?&lt;/h2&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/imgly-team/vibe-coding/recording-of-SF-Symbols-app.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;At their core, variable SF Symbols behave much like variable fonts: they allow you to interpolate smoothly between multiple “master” designs, rather than shipping separate static assets for each weight. To create one, you supply three distinct versions of your icon (typically &lt;em&gt;Ultra Light&lt;/em&gt;, &lt;em&gt;Regular&lt;/em&gt;, and &lt;em&gt;Black&lt;/em&gt;) each sharing the exact same path structure and node count. When the SF Symbols app or iOS runtime renders your symbol, it calculates intermediate weights on the fly by linearly interpolating between those three masters. This approach not only shrinks your asset footprint but also gives you rich typographic control, letting you fine-tune how heavy or light an icon appears in any context.&lt;/p&gt;
&lt;h2 id=&quot;the-early-struggle&quot;&gt;The Early Struggle&lt;/h2&gt;
&lt;p&gt;From the moment we discovered the Figma plugin &lt;a href=&quot;https://www.figma.com/community/plugin/1265484574589944515/sf-symbols-optimizer&quot;&gt;&lt;strong&gt;SF Symbols Organizer&lt;/strong&gt;&lt;/a&gt;, it became an indispensable part of our workflow. Suddenly, what had been a tedious, manual export process turned into a single click: select your three masters, batch-export them as SF Symbols-ready SVGs, and you were halfway home. The plugin’s enforcement of a 1:1 aspect-ratio rule was actually a blessing when you’re just getting started, ensuring that every icon fit out of the gate.&lt;/p&gt;
&lt;p&gt;However, as our icon library expanded beyond a handful of simple glyphs, we began to feel the boundaries of that magic square. Some of our more ambitious designs needed wider canvases or asymmetrical compositions, and shoehorning them into perfect squares led to awkward scaling and extra cropping steps. Meanwhile, although &lt;strong&gt;SF Symbols Organizer&lt;/strong&gt; reliably generated valid SVGs, it didn’t warn us about deeper issues (like mismatched node counts or flipped path directions) until we imported into the SF Symbols app and discovered the interpolation errors firsthand.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;This plugin exports icons created in Figma as a SF Symbol SVG bundle.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 293px) 293px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;293&quot; height=&quot;448&quot; src=&quot;https://img.ly/_astro/image_Z2wWyER.webp&quot; srcset=&quot;/_astro/image_Z2wWyER.webp 293w&quot;&gt;&lt;/p&gt;
&lt;p&gt;That’s where &lt;a href=&quot;https://www.figma.com/community/plugin/1391765568770221941/vector-path-editor&quot;&gt;Vector Path Editor&lt;/a&gt; pitched in. It couldn’t automate our entire workflow, but it did let us quickly correct path order and direction when Figma’s flattening shuffled our nodes. Even with that help, though, we still found ourselves jumping between tools to catch errors after the fact, proof that, while both plugins were fantastic at what they did, we needed something more proactive.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Change path order and direction (and fill rules if you need that 😉, too) with this plugin.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 395px) 395px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;395&quot; height=&quot;434&quot; src=&quot;https://img.ly/_astro/image-1_1WKno3.webp&quot; srcset=&quot;/_astro/image-1_1WKno3.webp 395w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Yet, as powerful as these two plugins were together, their reactive nature meant we still spent long sessions hopping between Figma and Apple’s toolchain, correcting mistakes after they happened instead of catching them up front. It was a great start (and without these plugins, we wouldn’t have made it this far), but we knew there had to be a way to bake most of the missing quality checks into our design process itself.&lt;/p&gt;
&lt;h2 id=&quot;why-we-wanted-sf-symbol-generator&quot;&gt;Why We Wanted “SF Symbol Generator”&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;Generate static of variable SF Symbols with the SF Symbol Generator plugin.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1920px) 1920px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1920&quot; height=&quot;1080&quot; src=&quot;https://img.ly/_astro/Slide-16_9---v1.0.0_Z7e9B1.webp&quot; srcset=&quot;/_astro/Slide-16_9---v1.0.0_HeJe4.webp 640w, /_astro/Slide-16_9---v1.0.0_Z1QODrK.webp 750w, /_astro/Slide-16_9---v1.0.0_DcwH3.webp 828w, /_astro/Slide-16_9---v1.0.0_1MEKoJ.webp 1080w, /_astro/Slide-16_9---v1.0.0_ZdeHmr.webp 1280w, /_astro/Slide-16_9---v1.0.0_22clUH.webp 1668w, /_astro/Slide-16_9---v1.0.0_Z7e9B1.webp 1920w&quot;&gt;&lt;/p&gt;
&lt;p&gt;After enough trial and error, we realized we needed more than just export and pray. We needed flexibility and foresight.&lt;/p&gt;
&lt;p&gt;First, we didn’t want our icons locked into a square. Some of our designs needed wider canvases. Second, we craved live feedback. We wanted to know if our three icons had matching node counts, aligned first nodes, and consistent path winding before exporting. Lastly, we longed for a single, unified workflow that could handle scanning, validating, and exporting without leaving Figma.&lt;/p&gt;
&lt;p&gt;It quickly became clear that the only way to achieve this was to build our own plugin. And so &lt;a href=&quot;https://www.figma.com/community/plugin/1487107089082698461/sf-symbol-generator&quot;&gt;SF Symbol Generator&lt;/a&gt; was born.&lt;/p&gt;
&lt;h2 id=&quot;inside-sf-symbol-generator&quot;&gt;Inside SF Symbol Generator&lt;/h2&gt;
&lt;p&gt;SF Symbol Generator transforms the way we build custom symbols. Rather than flattening paths prematurely, it encourages us to keep boolean groups intact, flattening only at export time. When you run the plugin, it scans all three masters in situ, then displays a detailed readout of node counts, first-node anchors, and path directions. Any discrepancies are highlighted in red, so you can go back to your designs and fix them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;*Pro Tip:&lt;/strong&gt; Sometimes Figma’s automatic flattening doesn’t handle rounded corners cleanly and can introduce extra nodes. If the plugin’s scan flags a weight with mismatched node counts or errant anchors, you can manually flatten that master ahead of time, clean up extra nodes using Figma’s vector editing tools, and then re-run the scan to ensure perfect parity.*&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;For simple bezier curves, Figma sometimes still adds extra nodes that need cleaning up.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2010px) 2010px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2010&quot; height=&quot;907&quot; src=&quot;https://img.ly/_astro/image-2_ZoM3NN.webp&quot; srcset=&quot;/_astro/image-2_1JkJvl.webp 640w, /_astro/image-2_Z1slfHO.webp 750w, /_astro/image-2_1uSgfe.webp 828w, /_astro/image-2_YgQD1.webp 1080w, /_astro/image-2_Z1o8UVl.webp 1280w, /_astro/image-2_Z1dkp9e.webp 1668w, /_astro/image-2_ZoM3NN.webp 2010w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once everything checks out, a single click exports a ready-to-use variable SVG bundle, no detours into external editors required.&lt;/p&gt;
&lt;h2 id=&quot;our-step-by-step-workflow&quot;&gt;Our Step-By-Step Workflow&lt;/h2&gt;
&lt;p&gt;Our process now is refreshingly simple. We start in Figma by creating a 32-pixel-tall frame that can have any width you need.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Start with a ∞×32 Frame with a safe-zone of 4px.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2960px) 2960px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2960&quot; height=&quot;1284&quot; src=&quot;https://img.ly/_astro/image-3_1flMEA.webp&quot; srcset=&quot;/_astro/image-3_28MgYc.webp 640w, /_astro/image-3_Z4T9gh.webp 750w, /_astro/image-3_Z13kX07.webp 828w, /_astro/image-3_Z2aMo2a.webp 1080w, /_astro/image-3_t9L2S.webp 1280w, /_astro/image-3_kU3ws.webp 1668w, /_astro/image-3_ZO6Q9W.webp 2048w, /_astro/image-3_Z1mwW5j.webp 2560w, /_astro/image-3_1flMEA.webp 2960w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Inside that frame, we draw our Regular-weight icon using a 2.45-pixel stroke and a 3-pixel corner radius. We then duplicate the frame twice and adjust the strokes for Ultra Light (0.8 px, 2.5 px radius) and Black (4.95 px, 3.5 px radius). iOS commonly incorporates softer corners in their icon design, and you can achieve them with Figma’s corner smoothing. Though, it’s important to keep Figma’s corner smoothing at 0%, because even a sliver of smoothing can throw off interpolation. We also center-align our strokes wherever possible so that the shapes grow uniformly when the weight changes.&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Stroke size&lt;/th&gt;&lt;th&gt;Border radius&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Ultra Thin&lt;/td&gt;&lt;td&gt;0.8&lt;/td&gt;&lt;td&gt;2.5&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Regular&lt;/td&gt;&lt;td&gt;2.45&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Black&lt;/td&gt;&lt;td&gt;4.95–3.5&lt;/td&gt;&lt;td&gt;3.5&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;img alt=&quot;Three weights are needed for SF Symbol Generator to interpolate between them.&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;868&quot; src=&quot;https://img.ly/_astro/image-4_VuTSM.webp&quot; srcset=&quot;/_astro/image-4_23W4fP.webp 640w, /_astro/image-4_ZDJ8XY.webp 750w, /_astro/image-4_Z18baIt.webp 828w, /_astro/image-4_s5k1I.webp 1080w, /_astro/image-4_Z1X9DHa.webp 1280w, /_astro/image-4_Z276p0I.webp 1668w, /_astro/image-4_VuTSM.webp 2000w&quot;&gt;&lt;/p&gt;
&lt;p&gt;With your three master frames selected, launch SF Symbol Generator. In the plugin’s Variable panel, assign each frame to its corresponding weight: Ultra Light, Regular, and Black. If your frames are named correctly (for example, “Icon Ultra Light,” “Icon Regular,” “Icon Black”), the plugin will detect and map them automatically. As soon as each weight is assigned, the previews immediately show node counts, first-node positions, and path directions. Any mismatches appear in red, at which point you might need to go back and manually flatten that master, clean up extra nodes (for example, Figma sometimes adds unwanted anchors on rounded corners), and re-scan them again. When everything looks great, just hit Export to generate your ready-to-use variable SVG bundle.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/imgly-team/vibe-coding/using-SF-Symbol-generator-on-3-Icons.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h2 id=&quot;importing-into-the-sf-symbols-app&quot;&gt;Importing into the SF Symbols App&lt;/h2&gt;
&lt;p&gt;Once your variable SVG bundle is exported, the final import into Apple’s SF Symbols app is straightforward. Open the SF Symbols application (available on Apple’s developer site), switch to the &lt;strong&gt;Custom Symbols&lt;/strong&gt; tab, and drag your SVG file into the panel. Your new icons will appear alongside Apple’s built-in set, ready to be organized into collections or shared with teammates. When you return to Xcode, your custom symbols show up just like any system glyph, complete with full support for dynamic weights.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/imgly-team/vibe-coding/adding-custom-symbol-to-SF-symbols.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h3 id=&quot;optional-extra-step-preparing-more-complex-symbols&quot;&gt;Optional Extra Step: Preparing More Complex Symbols&lt;/h3&gt;
&lt;p&gt;If your icon consists of multiple layers or requires special styling per layer, take this extra step to ensure it renders perfectly in all weights. After dragging your SVG into the SF Symbols app:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select your newly imported symbol and click the &lt;strong&gt;Rendering Inspector&lt;/strong&gt; &lt;em&gt;(the segmented button with the brush icon)&lt;/em&gt; on the right side.&lt;/li&gt;
&lt;li&gt;You’ll see each layer exposed as a separate sub-path. You can create more layers and even group them to change them individually as well.&lt;/li&gt;
&lt;li&gt;Tweak opacity, colors, or layer order at different render modes to achieve the exact look you want.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;Use the SF Symbols App to prepare your more complex symbols accordingly.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1347px) 1347px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1347&quot; height=&quot;953&quot; src=&quot;https://img.ly/_astro/image-5_eYkB2.webp&quot; srcset=&quot;/_astro/image-5_3eo08.webp 640w, /_astro/image-5_2rQrVD.webp 750w, /_astro/image-5_10OVMI.webp 828w, /_astro/image-5_LLzpn.webp 1080w, /_astro/image-5_Z2qyB31.webp 1280w, /_astro/image-5_eYkB2.webp 1347w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once you’re satisfied, select and export the symbol again from the SF Symbols app &lt;em&gt;(File &gt; Export Symbol)&lt;/em&gt;, and it’s truly ready for production.&lt;/p&gt;
&lt;h2 id=&quot;lessons-learned&quot;&gt;Lessons Learned&lt;/h2&gt;
&lt;p&gt;Building SF Symbol Generator taught us several key lessons.&lt;br&gt;
First, node parity is king: the number and order of path segments must match across masters. Early validation saves hours, since catching mismatches before export means fewer cycles in Apple’s SF Symbols app.&lt;br&gt;
Second, non-destructive workflows pay dividends: boolean groups let you iterate quickly without losing stroke data.&lt;br&gt;
And finally, automation is your friend: what once took dozens of manual steps now happens in a single click.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Variable SF Symbols unlock a level of typographic and iconographic dynamism that feels truly native to iOS. While community tools like SF Symbols Organizer and Vector Path Editor can get you pretty far already, building our own SF Symbol Generator transformed our workflow from reactive to proactive. Now, we design freely with any aspect ratio, validate instantly, and export with confidence: no more praying, just seamless symbol-making.&lt;/p&gt;
&lt;p&gt;For our CE.SDK for iOS, we’ll be gradually rolling out updated Custom Symbols created with &lt;strong&gt;SF Symbol Generator&lt;/strong&gt;, ensuring every icon feels right at home on Apple devices. Stay tuned for those releases!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;We are gradually updating our old Symbols with newer ones – that look and feel more native.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;642&quot; src=&quot;https://img.ly/_astro/Image-8_nvFqP.webp&quot; srcset=&quot;/_astro/Image-8_Z13IVMv.webp 640w, /_astro/Image-8_1MKKKW.webp 750w, /_astro/Image-8_OjW27.webp 828w, /_astro/Image-8_9Nu5F.webp 1080w, /_astro/Image-8_Z2gqtDd.webp 1280w, /_astro/Image-8_nvFqP.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you’re ready to streamline your own custom SF Symbol workflow, check out &lt;a href=&quot;https://www.figma.com/community/plugin/1487107089082698461/sf-symbol-generator&quot;&gt;&lt;strong&gt;SF Symbol Generator&lt;/strong&gt;&lt;/a&gt; on the Figma Community and take it for a spin. Happy symbol-making!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;3,000+ creative professionals gain early access to new features and updates. Don’t miss out, and&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i?ref=img.ly&quot;&gt;&lt;strong&gt;subscribe&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;to our newsletter.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Dustin</dc:creator><media:content url="https://blog.img.ly/2025/05/Building-Custom-Variable-SF-Symbols-in-Figma-for-our-iOS-SDK-2.png" medium="image"/><category>iOS</category><category>Figma</category><category>Swift</category><category>SF Symbols</category></item></channel></rss>