<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ayaz</title>
    <description>The latest articles on DEV Community by ayaz (@ayarse).</description>
    <link>https://dev.to/ayarse</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F171823%2Fa4078a50-57a8-4095-ac1a-6c34b11eaf11.jpeg</url>
      <title>DEV Community: ayaz</title>
      <link>https://dev.to/ayarse</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ayarse"/>
    <language>en</language>
    <item>
      <title>Introducing Popkorn: a portable, CSS-based format for interactive motion graphics</title>
      <dc:creator>ayaz</dc:creator>
      <pubDate>Mon, 13 Jul 2026 18:51:04 +0000</pubDate>
      <link>https://dev.to/ayarse/introducing-popkorn-a-portable-css-based-format-for-interactive-motion-graphics-552j</link>
      <guid>https://dev.to/ayarse/introducing-popkorn-a-portable-css-based-format-for-interactive-motion-graphics-552j</guid>
      <description>&lt;p&gt;CSS is capable of expressing a lot when it comes to graphics, especially animation. &lt;a href="https://ayarse.github.io/popkorn" rel="noopener noreferrer"&gt;Popkorn&lt;/a&gt; takes that idea and runs with it: a portable &lt;strong&gt;format + runtime&lt;/strong&gt; for motion graphics, where you write a scene in syntax you already know and run the same file on the web and on native mobile.&lt;/p&gt;

&lt;p&gt;It renders some genuinely complex stuff, and it brings in almost any Lottie file without breaking anything, usually at a lower file size than the source. And it isn't just playback: scenes can be genuinely interactive, with &lt;code&gt;:hover&lt;/code&gt; and &lt;code&gt;:active&lt;/code&gt; states, state machines, and live inputs, all without a line of scripting. It started as a personal what-if and turned into a proof of concept that went further than I expected. It's still very early, but the idea clearly works.&lt;/p&gt;

&lt;h2&gt;
  
  
  A complete scene
&lt;/h2&gt;

&lt;p&gt;Here's an entire Popkorn scene: a ball, falling and bouncing, that warms in color when you point at it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1a1a2e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;bounce&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;animation-timing-function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cubic-bezier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;50&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;180px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;animation-timing-function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cubic-bezier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.67&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;#ball&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;circle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;36px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ff6b6b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bounce&lt;/span&gt; &lt;span class="m"&gt;1.2s&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fill&lt;/span&gt; &lt;span class="m"&gt;250ms&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="err"&gt;&amp;amp;:hover&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffd166&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you've written CSS, you can already read every line. Point at the ball and the &lt;code&gt;transition&lt;/code&gt; warms its color while the bounce never pauses or restarts. That's one of the nicer surprises: interactive states like &lt;code&gt;:hover&lt;/code&gt; compose &lt;em&gt;on top of&lt;/em&gt; a running animation instead of fighting it, because the whole scene plays on one continuous timeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why CSS
&lt;/h2&gt;

&lt;p&gt;This is the whole idea, not a shortcut. Popkorn holds itself to one rule: if CSS already has a way to express something, use it, with its real semantics. Motion paths are &lt;code&gt;offset-path&lt;/code&gt;. Holds are &lt;code&gt;step-end&lt;/code&gt;. Staggering is a negative &lt;code&gt;animation-delay&lt;/code&gt;. Layering is &lt;code&gt;z-index&lt;/code&gt;. It never invents syntax CSS already has. (It isn't &lt;em&gt;exactly&lt;/em&gt; CSS. It's a close dialect, kept as near to the real thing as possible, and maybe some good parts go upstream one day 🤞)&lt;/p&gt;

&lt;p&gt;Staying this close buys something rare: one format two very different audiences already read.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;People already speak it.&lt;/strong&gt; There's a vibrant community making genuinely beautiful art in hand-written CSS. Popkorn meets them where they are, with no new mental model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language models already speak it too, for free.&lt;/strong&gt; Because the format is CSS-shaped, a model writes valid Popkorn from its existing training data with minimal guidance. No fine-tuned model, no bespoke format to teach. The playground's example gallery is itself entirely LLM-generated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The payoff is a format that's hand-authorable, diffable in a pull request, and generatable by an LLM, all at once. And because scenes are just &lt;code&gt;.css&lt;/code&gt;, you get syntax highlighting free on GitHub and in every editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bring your own Lottie, then just ask for changes
&lt;/h2&gt;

&lt;p&gt;Here's the part I find most fun. Drop an existing Lottie file (or an SVG) into the playground and it converts, unchanged, into a readable Popkorn scene that plays back faithfully. No re-authoring, no cleanup pass. But now it's plain, CSS-shaped text, so you can keep going: ask the Copilot to recolor it, slow the timing, change the motion, or wire in a &lt;code&gt;:hover&lt;/code&gt; state, all in natural language, on a file you can also open and edit by hand. An opaque Lottie JSON blob becomes something you can actually converse with.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/XcvsOkNNqtw"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What it can do today
&lt;/h2&gt;

&lt;p&gt;It already renders vector shapes, gradients, masks, motion paths, and path morphing, with &lt;code&gt;@keyframes&lt;/code&gt; animation, per-keyframe easing, and holds. It goes beyond playback too: &lt;code&gt;:hover&lt;/code&gt; and &lt;code&gt;:active&lt;/code&gt; just work, and hand-written state machines drive toggles, taps, and app-state behavior with no scripting. It even imports real Lottie files and SVGs into readable scenes you can tweak on the spot, often coming out a touch smaller than the source they came from.&lt;/p&gt;

&lt;p&gt;In the browser it works well, with two renderers behind it (Canvas2D and SVG). The same scenes run on mobile through a React Native + Skia renderer. It's early: the surface is still growing and the clearest frontier is performance work. But the idea, that a CSS animation can be a portable artifact, is looking promising.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;No install, it runs in your browser: &lt;strong&gt;&lt;a href="https://ayarse.github.io/popkorn" rel="noopener noreferrer"&gt;ayarse.github.io/popkorn&lt;/a&gt;&lt;/strong&gt;. Edit the example scenes live, prompt the built-in Copilot to build one from a description, or drop in a Lottie or SVG and watch it convert. The code lives on &lt;a href="https://github.com/ayarse/popkorn" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; under MIT.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next, and getting involved
&lt;/h2&gt;

&lt;p&gt;The near-term focus is performance: deeper optimization and benchmarking, and hardening the native renderer. Longer term, the interesting questions are about the format itself, how far a close CSS dialect can stretch before it needs to invent something new, and which of those good parts might belong in real CSS one day.&lt;/p&gt;

&lt;p&gt;This is a what-if that turned out to work, shared in case the idea is as interesting to you as it was to build. If you make something with it, break it, or just have a strong opinion about the syntax, I'd love to hear it. Feedback and curiosity welcome.&lt;/p&gt;

</description>
      <category>css</category>
      <category>lottie</category>
      <category>animation</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
