Baking physics in CSS via linear() abuse

<p>Check out the following CSS animations (touch to play):</p> <div data-rss="only">these are CSS-based graphics and animations, you probably need to visit the website to see</div><div id="pendulum-example"> <div class="pivot"> <div class="arm1"> <div class="arm2"></div> </div> </div> </div><div id="ball-example"> <div class="ball ball-0"><div class="line"></div></div> <div class="ball ball-1"><div class="line"></div></div> <div class="ball ball-2"><div class="line"></div></div> <div class="ball ball-3"><div class="line"></div></div> </div><div id="particles-example"> <div class="p p-0"></div> <div class="p p-1"></div> <div class="p p-2"></div> <div class="p p-3"></div> <div class="p p-4"></div> <div class="p p-5"></div> <div class="p p-6"></div> <div class="p p-7"></div> <div class="p p-8"></div> <div class="p p-9"></div> <div class="p p-10"></div> <div class="p p-11"></div> <div class="p p-12"></div> <div class="p p-13"></div> <div class="p p-14"></div> <div class="p p-15"></div> <div class="p p-16"></div> <div class="p p-17"></div> <div class="p p-18"></div> <div class="p p-19"></div> <div class="p p-20"></div> <div class="p p-21"></div> <div class="p p-22"></div> <div class="p p-23"></div> <div class="p p-24"></div> <div class="p p-25"></div> <div class="p p-26"></div> <div class="p p-27"></div> <div class="p p-28"></div> <div class="p p-29"></div> <div class="p p-30"></div> <div class="p p-31"></div> <div class="p p-32"></div> <div class="p p-33"></div> <div class="p p-34"></div> <div class="p p-35"></div> <div class="p p-36"></div> <div class="p p-37"></div> <div class="p p-38"></div> <div class="p p-39"></div> <div class="p p-40"></div> <div class="p p-41"></div> <div class="p p-42"></div> <div class="p p-43"></div> <div class="p p-44"></div> <div class="p p-45"></div> <div class="p p-46"></div> <div class="p p-47"></div> <div class="p p-48"></div> <div class="p p-49"></div> <div class="p p-50"></div> </div><div id="confetti-example"> <div class="conf confetti-0"><div class="paper"></div></div> <div class="conf confetti-1"><div class="paper"></div></div> <div class="conf confetti-2"><div class="paper"></div></div> <div class="conf confetti-3"><div class="paper"></div></div> <div class="conf confetti-4"><div class="paper"></div></div> <div class="conf confetti-5"><div class="paper"></div></div> <div class="conf confetti-6"><div class="paper"></div></div> <div class="conf confetti-7"><div class="paper"></div></div> <div class="conf confetti-8"><div class="paper"></div></div> <div class="conf confetti-9"><div class="paper"></div></div> <div class="conf confetti-10"><div class="paper"></div></div> <div class="conf confetti-11"><div class="paper"></div></div> <div class="conf confetti-12"><div class="paper"></div></div> <div class="conf confetti-13"><div class="paper"></div></div> <div class="conf confetti-14"><div class="paper"></div></div> <div class="conf confetti-15"><div class="paper"></div></div> <div class="conf confetti-16"><div class="paper"></div></div> <div class="conf confetti-17"><div class="paper"></div></div> <div class="conf confetti-18"><div class="paper"></div></div> <div class="conf confetti-19"><div class="paper"></div></div> </div> <p>If you wanted to implement complex procedural animations (like physics) on the web, you’d probably go for JS-based <code>requestAnimationFrame</code> update-loop and animate each frame. But everyone knows that CSS animations are much smoother than janky frame-based JS animations.</p> <p>Native web animations run outside the main thread, so things like heavy JS computations won’t affect it. Likewise, it won’t interfere with your UI event handlers that decides the UI’s responsiveness.</p> <pre>Interactive content: <a href="https://leanrada.com/notes/baking-physics-css-linear-abuse/?ref=rss">Visit the post to interact with this content.</a> Alternative text: JS vs CSS animation comparison</pre> <p>However, we can’t do procedural animations in CSS. CSS is declarative.</p> <p>One way to do this is to precalculate the keyframes to replay with CSS, but a high quality replay would normally require a heavy amount of generated CSS to be loaded and parsed. I found a way to highly compress this precalculated CSS to a manageable size.</p> <h2>Technique</h2> <p>Baking precalculated animations in CSS keyframes is nothing new I suppose, but I wanted to highlight a ‘recent’ development in CSS, the <strong><code>linear()</code></strong> custom easing. Abusing this function enables high-fidelity CSS animations in a compact representation!</p> <p>First, the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/easing-function/linear" target="_blank"><code>linear()</code></a> custom ease (not to be confused with the classic <code>linear</code> keyword with no arguments) lets you define custom easing curves.</p> <pre><code><span class="token selector">div</span> <span class="token punctuation">{</span> <span class="token property">animation</span><span class="token punctuation">:</span> slide 2s <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.5 60%<span class="token punctuation">,</span> 0.25<span class="token punctuation">,</span> 1<span class="token punctuation">)</span> infinite alternate<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> slide</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> 20px <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> 120px <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <div>div</div> <p>It’s not very intuitive since we’re manually mapping values in the time domain (that’s what ease functions do). To illustrate what’s happening and to compare, we can denormalise the above example to classic keyframes that do the same thing:</p> <pre><code><span class="token selector">div</span> <span class="token punctuation">{</span> <span class="token property">animation</span><span class="token punctuation">:</span> slide 2s linear infinite alternate<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> slide</span> <span class="token punctuation">{</span> <span class="token selector">0%</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> 20px <span class="token punctuation">}</span> <span class="token selector">60%</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> 70px <span class="token punctuation">}</span> <span class="token selector">80%</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> 45px <span class="token punctuation">}</span> <span class="token selector">100%</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> 120px <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <div>div</div> <p>What <code>linear()</code> gives us is a compact form of defining single-property animations that have multiple keyframes, just like the above.</p> <p>A more typical example can be seen below which would’ve taken hundreds of bytes to write in classic keyframes:</p> <pre><code><span class="token selector">div</span> <span class="token punctuation">{</span> <span class="token property">animation</span><span class="token punctuation">:</span> wave 3s <span class="token function">linear</span><span class="token punctuation">(</span>0.5<span class="token punctuation">,</span> 0.578<span class="token punctuation">,</span> 0.655<span class="token punctuation">,</span> 0.727<span class="token punctuation">,</span> 0.794<span class="token punctuation">,</span> 0.854<span class="token punctuation">,</span> 0.905<span class="token punctuation">,</span> 0.946<span class="token punctuation">,</span> 0.976<span class="token punctuation">,</span> 0.994<span class="token punctuation">,</span> 1<span class="token punctuation">,</span> 0.994<span class="token punctuation">,</span> 0.976<span class="token punctuation">,</span> 0.946<span class="token punctuation">,</span> 0.905<span class="token punctuation">,</span> 0.854<span class="token punctuation">,</span> 0.794<span class="token punctuation">,</span> 0.727<span class="token punctuation">,</span> 0.655<span class="token punctuation">,</span> 0.578<span class="token punctuation">,</span> 0.5<span class="token punctuation">,</span> 0.422<span class="token punctuation">,</span> 0.345<span class="token punctuation">,</span> 0.273<span class="token punctuation">,</span> 0.206<span class="token punctuation">,</span> 0.146<span class="token punctuation">,</span> 0.095<span class="token punctuation">,</span> 0.054<span class="token punctuation">,</span> 0.024<span class="token punctuation">,</span> 0.006<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0.006<span class="token punctuation">,</span> 0.024<span class="token punctuation">,</span> 0.054<span class="token punctuation">,</span> 0.095<span class="token punctuation">,</span> 0.146<span class="token punctuation">,</span> 0.206<span class="token punctuation">,</span> 0.273<span class="token punctuation">,</span> 0.345<span class="token punctuation">,</span> 0.422<span class="token punctuation">,</span> 0.5<span class="token punctuation">)</span> infinite alternate<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> wave</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> 0 0 <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> 0 50px <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <div class="container"> <div class="thediv">div</div> </div> <p>Above is a sampled sine wave. The range of positions over time (min and max) becomes the <code>from</code> and <code>to</code> values. At every sample, the position is recorded and normalised to [0,1] which becomes the <code>linear()</code> function’s values.</p> Note that the box starts in the <em>middle</em> of the range, not at <code>from</code>. The endpoints don’t have to be where the motion begins and ends. <h2>Easing function as data</h2> <p><strong>The main idea is we are no longer using the easing function to <em>ease</em> motion</strong> — how smoothly a thing goes from A to B. Think of it now as the actual motion being defined <em>inside</em> the easing function.</p> <p>The <code>linear()</code> function contains the motion data. And the <code>@keyframes</code> declarations only give the absolute range that the motion data operates over.</p> <p>So far we’ve only been working with one axis or degree of freedom. If we wanted a more interesting physics simulation, we’d need more dimensions, and a way to combine their animations.</p> <h2>Composition</h2> <p>For 2D animation, we need two separately-animated dimensions, but right now it’s written in a single property, <code>translate: x y</code>. Having two animations run on the same property would overwrite the other.</p> <p>The general answer to this is <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/animation-composition" target="_blank"><code>animation-composition</code></a>, but it forces the animation to calculate in the main thread, and we lose the main selling point of smooth animations.</p> <p>One way out is to use two technically different properties for translation, <code>translate</code> and <code>transform</code>. Note that transformation properties are applied in this order: <code>translate</code>, <code>rotate</code>, <code>scale</code>, <code>transform</code>.</p> <p>So x can use <code>translate: $x 0</code> and y can use <code>transform: translateY($y)</code>:</p> <pre><code><span class="token selector">div</span> <span class="token punctuation">{</span> <span class="token property">animation</span><span class="token punctuation">:</span> circle-x 2s <span class="token function">linear</span><span class="token punctuation">(</span>1<span class="token punctuation">,</span> 0.994<span class="token punctuation">,</span> 0.976<span class="token punctuation">,</span> 0.946<span class="token punctuation">,</span> 0.905<span class="token punctuation">,</span> 0.854<span class="token punctuation">,</span> 0.794<span class="token punctuation">,</span> 0.727<span class="token punctuation">,</span> 0.655<span class="token punctuation">,</span> 0.578<span class="token punctuation">,</span> 0.5<span class="token punctuation">,</span> 0.422<span class="token punctuation">,</span> 0.345<span class="token punctuation">,</span> 0.273<span class="token punctuation">,</span> 0.206<span class="token punctuation">,</span> 0.146<span class="token punctuation">,</span> 0.095<span class="token punctuation">,</span> 0.054<span class="token punctuation">,</span> 0.024<span class="token punctuation">,</span> 0.006<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0.006<span class="token punctuation">,</span> 0.024<span class="token punctuation">,</span> 0.054<span class="token punctuation">,</span> 0.095<span class="token punctuation">,</span> 0.146<span class="token punctuation">,</span> 0.206<span class="token punctuation">,</span> 0.273<span class="token punctuation">,</span> 0.345<span class="token punctuation">,</span> 0.422<span class="token punctuation">,</span> 0.5<span class="token punctuation">,</span> 0.578<span class="token punctuation">,</span> 0.655<span class="token punctuation">,</span> 0.727<span class="token punctuation">,</span> 0.794<span class="token punctuation">,</span> 0.854<span class="token punctuation">,</span> 0.905<span class="token punctuation">,</span> 0.946<span class="token punctuation">,</span> 0.976<span class="token punctuation">,</span> 0.994<span class="token punctuation">,</span> 1<span class="token punctuation">)</span> infinite<span class="token punctuation">,</span> circle-y 2s <span class="token function">linear</span><span class="token punctuation">(</span>0.5<span class="token punctuation">,</span> 0.578<span class="token punctuation">,</span> 0.655<span class="token punctuation">,</span> 0.727<span class="token punctuation">,</span> 0.794<span class="token punctuation">,</span> 0.854<span class="token punctuation">,</span> 0.905<span class="token punctuation">,</span> 0.946<span class="token punctuation">,</span> 0.976<span class="token punctuation">,</span> 0.994<span class="token punctuation">,</span> 1<span class="token punctuation">,</span> 0.994<span class="token punctuation">,</span> 0.976<span class="token punctuation">,</span> 0.946<span class="token punctuation">,</span> 0.905<span class="token punctuation">,</span> 0.854<span class="token punctuation">,</span> 0.794<span class="token punctuation">,</span> 0.727<span class="token punctuation">,</span> 0.655<span class="token punctuation">,</span> 0.578<span class="token punctuation">,</span> 0.5<span class="token punctuation">,</span> 0.422<span class="token punctuation">,</span> 0.345<span class="token punctuation">,</span> 0.273<span class="token punctuation">,</span> 0.206<span class="token punctuation">,</span> 0.146<span class="token punctuation">,</span> 0.095<span class="token punctuation">,</span> 0.054<span class="token punctuation">,</span> 0.024<span class="token punctuation">,</span> 0.006<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0.006<span class="token punctuation">,</span> 0.024<span class="token punctuation">,</span> 0.054<span class="token punctuation">,</span> 0.095<span class="token punctuation">,</span> 0.146<span class="token punctuation">,</span> 0.206<span class="token punctuation">,</span> 0.273<span class="token punctuation">,</span> 0.345<span class="token punctuation">,</span> 0.422<span class="token punctuation">,</span> 0.5<span class="token punctuation">)</span> infinite<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> circle-x</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> 0 0 <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> 100px 0 <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> circle-y</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>0<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>100px<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <div class="container"> <div class="thediv">div</div> </div> <p>We can now animate two dimensions independently. Each <code>linear()</code> gives the motion data for the corresponding dimension.</p> <p><code>transform</code> is applied last, after <code>rotate</code>, which means adding a rotation dimension to the animation would cause issues.</p> <p>The solution for sane rotate and scale animations is div wrappers: Apply translation to the parent element, and only apply rotation and scale to the child element.</p> <p>Again, all of this is still possible with classic keyframes, but that would require explicitly writing <em>both coordinates in every frame</em>. In animations where x and y are mostly independent, that is highly inefficient. It’s a multiplicative gain in efficiency.</p> <h2>Reuse</h2> <p>Multiple elements can reuse the same <code>@keyframes</code> declarations. Since the only purpose of <code>@keyframes</code> now is to define the <em>range</em> of values, we can get the union of ranges <em>over all elements</em> so all elements can work within the same animation range — the same <code>@keyframes</code> declaration. Individual movements are still encoded in the respective <code>linear()</code>.</p> <pre><code><span class="token selector">.a</span> <span class="token punctuation">{</span> <span class="token property">animation</span><span class="token punctuation">:</span> bounce-x 2s <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 1<span class="token punctuation">,</span> 0.84<span class="token punctuation">)</span> infinite<span class="token punctuation">,</span> bounce-y 2s <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.009<span class="token punctuation">,</span> 0.028<span class="token punctuation">,</span> 0.056<span class="token punctuation">,</span> 0.094<span class="token punctuation">,</span> 0.141<span class="token punctuation">,</span> 0.197<span class="token punctuation">,</span> 0.263<span class="token punctuation">,</span> 0.338<span class="token punctuation">,</span> 0.422<span class="token punctuation">,</span> 0.516<span class="token punctuation">,</span> 0.619<span class="token punctuation">,</span> 0.731<span class="token punctuation">,</span> 0.853<span class="token punctuation">,</span> 0.984<span class="token punctuation">,</span> 0.906<span class="token punctuation">,</span> 0.81<span class="token punctuation">,</span> 0.723<span class="token punctuation">,</span> 0.646<span class="token punctuation">,</span> 0.578<span class="token punctuation">,</span> 0.52<span class="token punctuation">,</span> 0.47<span class="token punctuation">,</span> 0.43<span class="token punctuation">,</span> 0.4<span class="token punctuation">,</span> 0.379<span class="token punctuation">,</span> 0.367<span class="token punctuation">,</span> 0.365<span class="token punctuation">,</span> 0.372<span class="token punctuation">,</span> 0.388<span class="token punctuation">,</span> 0.414<span class="token punctuation">,</span> 0.449<span class="token punctuation">,</span> 0.494<span class="token punctuation">,</span> 0.548<span class="token punctuation">,</span> 0.611<span class="token punctuation">,</span> 0.684<span class="token punctuation">,</span> 0.766<span class="token punctuation">,</span> 0.857<span class="token punctuation">,</span> 0.958<span class="token punctuation">,</span> 0.949<span class="token punctuation">,</span> 0.876<span class="token punctuation">,</span> 0.812<span class="token punctuation">,</span> 0.757<span class="token punctuation">,</span> 0.712<span class="token punctuation">,</span> 0.677<span class="token punctuation">,</span> 0.65<span class="token punctuation">,</span> 0.633<span class="token punctuation">,</span> 0.626<span class="token punctuation">,</span> 0.627<span class="token punctuation">,</span> 0.638<span class="token punctuation">,</span> 0.659<span class="token punctuation">,</span> 0.689<span class="token punctuation">,</span> 0.728<span class="token punctuation">,</span> 0.777<span class="token punctuation">,</span> 0.835<span class="token punctuation">,</span> 0.902<span class="token punctuation">,</span> 0.979<span class="token punctuation">,</span> 0.951<span class="token punctuation">,</span> 0.896<span class="token punctuation">,</span> 0.85<span class="token punctuation">,</span> 0.814<span class="token punctuation">,</span> 0.787<span class="token punctuation">,</span> 0.769<span class="token punctuation">,</span> 0.76<span class="token punctuation">,</span> 0.762<span class="token punctuation">,</span> 0.772<span class="token punctuation">,</span> 0.792<span class="token punctuation">,</span> 0.821<span class="token punctuation">,</span> 0.859<span class="token punctuation">,</span> 0.907<span class="token punctuation">,</span> 0.965<span class="token punctuation">,</span> 0.977<span class="token punctuation">,</span> 0.936<span class="token punctuation">,</span> 0.905<span class="token punctuation">,</span> 0.883<span class="token punctuation">,</span> 0.87<span class="token punctuation">,</span> 0.867<span class="token punctuation">,</span> 0.874<span class="token punctuation">,</span> 0.889<span class="token punctuation">,</span> 0.914<span class="token punctuation">,</span> 0.949<span class="token punctuation">,</span> 0.992<span class="token punctuation">)</span> infinite<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.b</span> <span class="token punctuation">{</span> <span class="token property">animation</span><span class="token punctuation">:</span> bounce-x 2s <span class="token function">linear</span><span class="token punctuation">(</span>1<span class="token punctuation">,</span> 0 50%<span class="token punctuation">,</span> 0<span class="token punctuation">)</span> infinite<span class="token punctuation">,</span> bounce-y 2s <span class="token function">linear</span><span class="token punctuation">(</span>0.5<span class="token punctuation">,</span> 0.506<span class="token punctuation">,</span> 0.517<span class="token punctuation">,</span> 0.534<span class="token punctuation">,</span> 0.556<span class="token punctuation">,</span> 0.584<span class="token punctuation">,</span> 0.618<span class="token punctuation">,</span> 0.657<span class="token punctuation">,</span> 0.703<span class="token punctuation">,</span> 0.753<span class="token punctuation">,</span> 0.809<span class="token punctuation">,</span> 0.871<span class="token punctuation">,</span> 0.939<span class="token punctuation">,</span> 0.993<span class="token punctuation">,</span> 0.955<span class="token punctuation">,</span> 0.922<span class="token punctuation">,</span> 0.895<span class="token punctuation">,</span> 0.874<span class="token punctuation">,</span> 0.858<span class="token punctuation">,</span> 0.848<span class="token punctuation">,</span> 0.843<span class="token punctuation">,</span> 0.844<span class="token punctuation">,</span> 0.851<span class="token punctuation">,</span> 0.863<span class="token punctuation">,</span> 0.881<span class="token punctuation">,</span> 0.905<span class="token punctuation">,</span> 0.934<span class="token punctuation">,</span> 0.969<span class="token punctuation">,</span> 0.994<span class="token punctuation">,</span> 0.975<span class="token punctuation">,</span> 0.962<span class="token punctuation">,</span> 0.955<span class="token punctuation">,</span> 0.953<span class="token punctuation">,</span> 0.957<span class="token punctuation">,</span> 0.966<span class="token punctuation">,</span> 0.982<span class="token punctuation">,</span> 0.999<span class="token punctuation">,</span> 0.992<span class="token punctuation">,</span> 0.991<span class="token punctuation">,</span> 0.995<span class="token punctuation">,</span> 1 50%<span class="token punctuation">,</span> 1<span class="token punctuation">)</span> infinite<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> bounce-x</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> 0 0 <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> 100px 0 <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> bounce-y</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>0<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>100px<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <div class="container"> <div class="world"> <div class="a">A</div> <div class="b">B</div> </div> </div> <p>This is a big CSS efficiency win. We only need to name the specific CSS property / dimension a constant number of times, and the actual motion data per element is defined separately as pure numbers. Think of a <code>@keyframes</code> declaration as a channel with <code>linear()</code> as the data.</p> <h2>Baking</h2> <details open="" style="color:var(--text2-clr)"> <summary>Directions</summary> <ol> <li>Beat butter, sugar, and vanilla extract until incorporated.</li> <li>Beat in egg yolks.</li> <li>Beat in half of the sifted flour, then the milk.</li> <li>Beat or stir in the remainder of the flour until <em>just</em> incorporated.</li> <li>Pour the batter(s) into a cake tin.</li> <li>Bake at 175°C/350°F (conventional non-fan-forced) for 40-60min.</li> </ol> </details> <p>Oh, right. <strong>Baking the motion data.</strong> It depends on what your animation is, really. If it’s a simulation, then you record the state of each object in each time step. Only record the state that has corresponding CSS properties (or, design your simulation state for CSSability in the first place). After sampling state over time, it’s just mapping the values to the linear() abuse format.</p> <p>To prepare for encoding, you’d need the total range of values for the <code>@keyframes</code> declarations. For each state <code>$dimension</code> you’re interested in, find the min and max values over the whole animation and use that in:</p> <pre><code><span class="token atrule"><span class="token rule">@keyframes</span> $dimension</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> $<span class="token property">property</span><span class="token punctuation">:</span> $min <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> $<span class="token property">property</span><span class="token punctuation">:</span> $max <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> Dimension can mean anything in your model that is animated independently, like x, y, color, rotation, scale, radius, progress, depth, etc. <p>To encode the <code>linear()</code> numbers: For every dimension, for every sample, normalise the property values against the range you got from earlier to [0,1]. Remember, one <code>animation</code>, one <code>linear()</code> per dimension!</p> <pre><code><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> dimension <span class="token keyword">of</span> dimensions<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> normalized <span class="token operator">=</span> dimension<span class="token punctuation">.</span>samples<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span> <span class="token parameter">sample</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span>sample <span class="token operator">-</span> dimension<span class="token punctuation">.</span>min<span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token punctuation">(</span>dimension<span class="token punctuation">.</span>max <span class="token operator">-</span> dimension<span class="token punctuation">.</span>min<span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> css <span class="token operator">+=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>dimension<span class="token punctuation">.</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> linear(</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>normalized<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">", "</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">),\n</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p>The following is a demo that bakes your mouse movements into your very own linear() abuse CSS animation in real time.</p> <p> </p> <div class="stage"> <div class="drag-me">drag me</div> </div> <p></p> <pre><code class="baked">CSS will appear here. Drag the draggy thing!</code></pre> <p>An optimisation could be added such that you skip writing samples that are too similar to the previous sample.</p> <p>We let <code>linear()</code> do its actual job of linearly interpolating your values.</p> <p>Skipping samples would break the property that each sample is in regular intervals, so you’d also need to add the progress percentage coordinates in <code>linear()</code> where necessary.</p> <p>If your animation is more of the hand-authored type, then you’re just mapping keyframes to keyframes. This could probably be automated depending on the source animation authoring format. Any easing / motion curves from the source material would have to be ‘rasterised’ though, because we can only do linear interpolation with <code>linear()</code>.</p> <h2>Alternatives considered / related tech</h2> <p><a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API" target="_blank"><strong>WAAPI.</strong></a> Web Animations API (WAAPI) is basically a JS API for creating CSS animations. It’s much better than dynamically constructing <code>&lt;style&gt;</code> tags. So I lied when I said JS-based animations can’t achieve native speeds (actually I didn’t, I specifically called out <code>requestAnimationFrame</code> only!).</p> <ul> <li>WAAPI is used by most animation frameworks nowadays such as <a href="https://motion.dev/" target="_blank">[Framer] Motion</a>. They have <a href="https://motion.dev/docs/performance" target="_blank">a great article explaining JS-based vs native animations</a>. </li><li>WAAPI is not an alternative per se. You can bake the simulation and replay it in either CSS or WAAPI, and get native speeds on dynamic/procedural animations either way. </li></ul> <p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/offset-path" target="_blank"><strong>offset-path</strong></a> is a strong alternative. It essentially lets you define an SVG-style path that an element could follow.</p> <ul> <li>Only limited to position (2 degrees of freedom), no other dimensions like color, opacity, or any other animatable CSS property. </li><li>Not as compact as linear() abuse (SVG path commands, absolute values, and not as code reuse-friendly). </li><li>Curves can be defined more concisely with path curve commands, unlike <code>linear()</code> stuck with linear interpolation. </li></ul> <p><strong>SVG animations?</strong> <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Guides/SVG_animation_with_SMIL" target="_blank">SMIL</a>, &lt;animateMotion&gt;? Idk enough about these, but I imagine it’s verbose XML declarative animations.</p> <p><strong>CSS custom animatable <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@property" target="_blank"><code>@property</code></a>.</strong> Apparently animating custom properties recalculates every frame in the main thread? So this isn’t very performant. <a href="https://www.bram.us/2023/02/01/the-gotcha-with-animating-custom-properties/" target="_blank">This blog</a> writes about it in detail.</p> <p><a href="https://leanrada.com/notes/css-sprite-sheets?ref=rss" target="_blank"><strong>Sprite sheets.</strong></a> Yeah sprite sheets.</p> <p><strong>prerendered video / animated WebP!</strong> This is the solution for 99% of cases! Forget everything you’ve learned in this blog, this is just some trifling for fun!</p> <h2>Demo code</h2> <p>Click a demo to see its CSS source code below the gallery!</p> <div style="cursor: pointer;"> <div class="pivot"> <div class="arm1"> <div class="arm2"></div> </div> </div> </div><div style="cursor: pointer;"> <div class="ball ball-0"><div class="line"></div></div> <div class="ball ball-1"><div class="line"></div></div> <div class="ball ball-2"><div class="line"></div></div> <div class="ball ball-3"><div class="line"></div></div> </div><div style="cursor: pointer;"> <div class="p p-0"></div> <div class="p p-1"></div> <div class="p p-2"></div> <div class="p p-3"></div> <div class="p p-4"></div> <div class="p p-5"></div> <div class="p p-6"></div> <div class="p p-7"></div> <div class="p p-8"></div> <div class="p p-9"></div> <div class="p p-10"></div> <div class="p p-11"></div> <div class="p p-12"></div> <div class="p p-13"></div> <div class="p p-14"></div> <div class="p p-15"></div> <div class="p p-16"></div> <div class="p p-17"></div> <div class="p p-18"></div> <div class="p p-19"></div> <div class="p p-20"></div> <div class="p p-21"></div> <div class="p p-22"></div> <div class="p p-23"></div> <div class="p p-24"></div> <div class="p p-25"></div> <div class="p p-26"></div> <div class="p p-27"></div> <div class="p p-28"></div> <div class="p p-29"></div> <div class="p p-30"></div> <div class="p p-31"></div> <div class="p p-32"></div> <div class="p p-33"></div> <div class="p p-34"></div> <div class="p p-35"></div> <div class="p p-36"></div> <div class="p p-37"></div> <div class="p p-38"></div> <div class="p p-39"></div> <div class="p p-40"></div> <div class="p p-41"></div> <div class="p p-42"></div> <div class="p p-43"></div> <div class="p p-44"></div> <div class="p p-45"></div> <div class="p p-46"></div> <div class="p p-47"></div> <div class="p p-48"></div> <div class="p p-49"></div> <div class="p p-50"></div> </div><div style="cursor: pointer;"> <div class="conf confetti-0"><div class="paper"></div></div> <div class="conf confetti-1"><div class="paper"></div></div> <div class="conf confetti-2"><div class="paper"></div></div> <div class="conf confetti-3"><div class="paper"></div></div> <div class="conf confetti-4"><div class="paper"></div></div> <div class="conf confetti-5"><div class="paper"></div></div> <div class="conf confetti-6"><div class="paper"></div></div> <div class="conf confetti-7"><div class="paper"></div></div> <div class="conf confetti-8"><div class="paper"></div></div> <div class="conf confetti-9"><div class="paper"></div></div> <div class="conf confetti-10"><div class="paper"></div></div> <div class="conf confetti-11"><div class="paper"></div></div> <div class="conf confetti-12"><div class="paper"></div></div> <div class="conf confetti-13"><div class="paper"></div></div> <div class="conf confetti-14"><div class="paper"></div></div> <div class="conf confetti-15"><div class="paper"></div></div> <div class="conf confetti-16"><div class="paper"></div></div> <div class="conf confetti-17"><div class="paper"></div></div> <div class="conf confetti-18"><div class="paper"></div></div> <div class="conf confetti-19"><div class="paper"></div></div> </div> <pre><code><span class="token atrule"><span class="token rule">@scope</span></span> <span class="token punctuation">{</span> <span class="token selector">:scope</span> <span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> 400px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 400px<span class="token punctuation">;</span> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span> <span class="token property">background</span><span class="token punctuation">:</span> #368a67<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 8px<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.pivot</span> <span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span> <span class="token property">left</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span> <span class="token property">top</span><span class="token punctuation">:</span> 35%<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.arm1, .arm2</span> <span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span> <span class="token property">left</span><span class="token punctuation">:</span> -2px<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> 4px<span class="token punctuation">;</span> <span class="token property">background</span><span class="token punctuation">:</span> #f8e6f2<span class="token punctuation">;</span> <span class="token property">transform-origin</span><span class="token punctuation">:</span> 2px 0<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.arm1::after, .arm2::after</span> <span class="token punctuation">{</span> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">;</span> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span> <span class="token property">left</span><span class="token punctuation">:</span> -8px<span class="token punctuation">;</span> <span class="token property">bottom</span><span class="token punctuation">:</span> -10px<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span> <span class="token property">background</span><span class="token punctuation">:</span> #f8e6f2<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> swing</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> <span class="token property">rotate</span><span class="token punctuation">:</span> -8.907rad <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> <span class="token property">rotate</span><span class="token punctuation">:</span> 40.439rad <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.arm1, .arm2</span> <span class="token punctuation">{</span> <span class="token property">animation</span><span class="token punctuation">:</span> swing 20s infinite<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.arm1</span> <span class="token punctuation">{</span> <span class="token property">height</span><span class="token punctuation">:</span> 110px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.2336 0%<span class="token punctuation">,</span> 0.2335 0.32%<span class="token punctuation">,</span> 0.2331 0.65%<span class="token punctuation">,</span> 0.2325 0.99%<span class="token punctuation">,</span> 0.2313 1.35%<span class="token punctuation">,</span> 0.2296 1.69%<span class="token punctuation">,</span> 0.2275 2%<span class="token punctuation">,</span> 0.2249 2.29%<span class="token punctuation">,</span> 0.2218 2.56%<span class="token punctuation">,</span> 0.2182 2.82%<span class="token punctuation">,</span> 0.2145 3.05%<span class="token punctuation">,</span> 0.2108 3.25%<span class="token punctuation">,</span> 0.2072 3.42%<span class="token punctuation">,</span> 0.2036 3.57%<span class="token punctuation">,</span> 0.1994 3.73%<span class="token punctuation">,</span> 0.191 4.02%<span class="token punctuation">,</span> 0.1872 4.16%<span class="token punctuation">,</span> 0.1837 4.29%<span class="token punctuation">,</span> 0.1805 4.43%<span class="token punctuation">,</span> 0.1773 4.57%<span class="token punctuation">,</span> 0.1742 4.71%<span class="token punctuation">,</span> 0.1713 4.86%<span class="token punctuation">,</span> 0.1686 5.03%<span class="token punctuation">,</span> 0.1663 5.2%<span class="token punctuation">,</span> 0.1648 5.35%<span class="token punctuation">,</span> 0.1639 5.5%<span class="token punctuation">,</span> 0.1638 5.65%<span class="token punctuation">,</span> 0.1643 5.81%<span class="token punctuation">,</span> 0.1656 6.05%<span class="token punctuation">,</span> 0.166 6.21%<span class="token punctuation">,</span> 0.1658 6.39%<span class="token punctuation">,</span> 0.165 6.59%<span class="token punctuation">,</span> 0.1634 6.83%<span class="token punctuation">,</span> 0.1611 7.09%<span class="token punctuation">,</span> 0.1583 7.36%<span class="token punctuation">,</span> 0.1552 7.66%<span class="token punctuation">,</span> 0.1518 8.01%<span class="token punctuation">,</span> 0.148 8.44%<span class="token punctuation">,</span> 0.1449 8.86%<span class="token punctuation">,</span> 0.142 9.35%<span class="token punctuation">,</span> 0.1402 9.72%<span class="token punctuation">,</span> 0.1386 10.03%<span class="token punctuation">,</span> 0.1372 10.32%<span class="token punctuation">,</span> 0.1354 10.66%<span class="token punctuation">,</span> 0.1341 10.97%<span class="token punctuation">,</span> 0.1338 11.2%<span class="token punctuation">,</span> 0.1341 11.41%<span class="token punctuation">,</span> 0.135 11.63%<span class="token punctuation">,</span> 0.1367 11.87%<span class="token punctuation">,</span> 0.139 12.11%<span class="token punctuation">,</span> 0.1419 12.34%<span class="token punctuation">,</span> 0.1451 12.54%<span class="token punctuation">,</span> 0.1483 12.71%<span class="token punctuation">,</span> 0.1515 12.85%<span class="token punctuation">,</span> 0.1556 13%<span class="token punctuation">,</span> 0.1603 13.17%<span class="token punctuation">,</span> 0.1635 13.29%<span class="token punctuation">,</span> 0.1666 13.42%<span class="token punctuation">,</span> 0.1698 13.56%<span class="token punctuation">,</span> 0.1729 13.71%<span class="token punctuation">,</span> 0.1761 13.85%<span class="token punctuation">,</span> 0.1792 13.99%<span class="token punctuation">,</span> 0.1824 14.13%<span class="token punctuation">,</span> 0.1857 14.27%<span class="token punctuation">,</span> 0.189 14.41%<span class="token punctuation">,</span> 0.1926 14.56%<span class="token punctuation">,</span> 0.1963 14.71%<span class="token punctuation">,</span> 0.2004 14.9%<span class="token punctuation">,</span> 0.2039 15.08%<span class="token punctuation">,</span> 0.2065 15.24%<span class="token punctuation">,</span> 0.2083 15.39%<span class="token punctuation">,</span> 0.2094 15.54%<span class="token punctuation">,</span> 0.2099 15.69%<span class="token punctuation">,</span> 0.2097 15.84%<span class="token punctuation">,</span> 0.209 15.98%<span class="token punctuation">,</span> 0.2077 16.12%<span class="token punctuation">,</span> 0.2058 16.24%<span class="token punctuation">,</span> 0.2029 16.39%<span class="token punctuation">,</span> 0.1997 16.54%<span class="token punctuation">,</span> 0.1975 16.67%<span class="token punctuation">,</span> 0.1954 16.82%<span class="token punctuation">,</span> 0.1934 17.03%<span class="token punctuation">,</span> 0.1916 17.26%<span class="token punctuation">,</span> 0.1904 17.52%<span class="token punctuation">,</span> 0.1897 17.8%<span class="token punctuation">,</span> 0.1896 18.16%<span class="token punctuation">,</span> 0.1893 18.43%<span class="token punctuation">,</span> 0.1885 18.65%<span class="token punctuation">,</span> 0.187 18.86%<span class="token punctuation">,</span> 0.185 19.06%<span class="token punctuation">,</span> 0.1826 19.23%<span class="token punctuation">,</span> 0.1801 19.38%<span class="token punctuation">,</span> 0.1777 19.49%<span class="token punctuation">,</span> 0.1751 19.59%<span class="token punctuation">,</span> 0.1686 19.8%<span class="token punctuation">,</span> 0.1661 19.91%<span class="token punctuation">,</span> 0.1637 20.03%<span class="token punctuation">,</span> 0.1617 20.17%<span class="token punctuation">,</span> 0.1602 20.32%<span class="token punctuation">,</span> 0.1593 20.47%<span class="token punctuation">,</span> 0.159 20.63%<span class="token punctuation">,</span> 0.1593 20.78%<span class="token punctuation">,</span> 0.1603 20.94%<span class="token punctuation">,</span> 0.1621 21.1%<span class="token punctuation">,</span> 0.1651 21.32%<span class="token punctuation">,</span> 0.1708 21.69%<span class="token punctuation">,</span> 0.1735 21.89%<span class="token punctuation">,</span> 0.1752 22.08%<span class="token punctuation">,</span> 0.1763 22.26%<span class="token punctuation">,</span> 0.1767 22.43%<span class="token punctuation">,</span> 0.1765 22.6%<span class="token punctuation">,</span> 0.1756 22.76%<span class="token punctuation">,</span> 0.1743 22.91%<span class="token punctuation">,</span> 0.1726 23.03%<span class="token punctuation">,</span> 0.1704 23.14%<span class="token punctuation">,</span> 0.166 23.35%<span class="token punctuation">,</span> 0.1642 23.46%<span class="token punctuation">,</span> 0.1629 23.58%<span class="token punctuation">,</span> 0.1622 23.71%<span class="token punctuation">,</span> 0.1621 23.85%<span class="token punctuation">,</span> 0.1626 23.99%<span class="token punctuation">,</span> 0.1638 24.13%<span class="token punctuation">,</span> 0.1657 24.28%<span class="token punctuation">,</span> 0.1685 24.45%<span class="token punctuation">,</span> 0.1724 24.64%<span class="token punctuation">,</span> 0.1764 24.82%<span class="token punctuation">,</span> 0.1802 24.98%<span class="token punctuation">,</span> 0.1837 25.13%<span class="token punctuation">,</span> 0.1871 25.27%<span class="token punctuation">,</span> 0.1903 25.42%<span class="token punctuation">,</span> 0.1935 25.58%<span class="token punctuation">,</span> 0.1967 25.75%<span class="token punctuation">,</span> 0.1998 25.93%<span class="token punctuation">,</span> 0.203 26.13%<span class="token punctuation">,</span> 0.2062 26.33%<span class="token punctuation">,</span> 0.2094 26.55%<span class="token punctuation">,</span> 0.2126 26.76%<span class="token punctuation">,</span> 0.2161 26.98%<span class="token punctuation">,</span> 0.2206 27.29%<span class="token punctuation">,</span> 0.2233 27.51%<span class="token punctuation">,</span> 0.2254 27.74%<span class="token punctuation">,</span> 0.227 27.98%<span class="token punctuation">,</span> 0.2279 28.22%<span class="token punctuation">,</span> 0.2282 28.45%<span class="token punctuation">,</span> 0.2279 28.67%<span class="token punctuation">,</span> 0.2269 28.87%<span class="token punctuation">,</span> 0.2253 29.07%<span class="token punctuation">,</span> 0.2232 29.26%<span class="token punctuation">,</span> 0.2203 29.46%<span class="token punctuation">,</span> 0.2165 29.68%<span class="token punctuation">,</span> 0.2121 29.9%<span class="token punctuation">,</span> 0.2082 30.08%<span class="token punctuation">,</span> 0.2046 30.24%<span class="token punctuation">,</span> 0.2012 30.39%<span class="token punctuation">,</span> 0.1979 30.53%<span class="token punctuation">,</span> 0.1945 30.67%<span class="token punctuation">,</span> 0.1913 30.81%<span class="token punctuation">,</span> 0.1881 30.94%<span class="token punctuation">,</span> 0.1849 31.06%<span class="token punctuation">,</span> 0.1817 31.17%<span class="token punctuation">,</span> 0.1784 31.27%<span class="token punctuation">,</span> 0.174 31.4%<span class="token punctuation">,</span> 0.1694 31.53%<span class="token punctuation">,</span> 0.1661 31.64%<span class="token punctuation">,</span> 0.1629 31.75%<span class="token punctuation">,</span> 0.1597 31.88%<span class="token punctuation">,</span> 0.1563 32.03%<span class="token punctuation">,</span> 0.1528 32.2%<span class="token punctuation">,</span> 0.1493 32.38%<span class="token punctuation">,</span> 0.1456 32.57%<span class="token punctuation">,</span> 0.1418 32.77%<span class="token punctuation">,</span> 0.1375 33.01%<span class="token punctuation">,</span> 0.1325 33.3%<span class="token punctuation">,</span> 0.1261 33.71%<span class="token punctuation">,</span> 0.1196 34.16%<span class="token punctuation">,</span> 0.1058 35.19%<span class="token punctuation">,</span> 0.1002 35.57%<span class="token punctuation">,</span> 0.0948 35.89%<span class="token punctuation">,</span> 0.0891 36.2%<span class="token punctuation">,</span> 0.0819 36.54%<span class="token punctuation">,</span> 0.0769 36.77%<span class="token punctuation">,</span> 0.0729 36.96%<span class="token punctuation">,</span> 0.0693 37.12%<span class="token punctuation">,</span> 0.066 37.28%<span class="token punctuation">,</span> 0.0628 37.44%<span class="token punctuation">,</span> 0.0596 37.6%<span class="token punctuation">,</span> 0.0564 37.76%<span class="token punctuation">,</span> 0.0534 37.9%<span class="token punctuation">,</span> 0.0504 38.03%<span class="token punctuation">,</span> 0.0475 38.13%<span class="token punctuation">,</span> 0.0445 38.24%<span class="token punctuation">,</span> 0.0371 38.46%<span class="token punctuation">,</span> 0.0341 38.56%<span class="token punctuation">,</span> 0.0312 38.68%<span class="token punctuation">,</span> 0.0281 38.84%<span class="token punctuation">,</span> 0.025 39.03%<span class="token punctuation">,</span> 0.0221 39.25%<span class="token punctuation">,</span> 0.0198 39.47%<span class="token punctuation">,</span> 0.0182 39.69%<span class="token punctuation">,</span> 0.0171 39.92%<span class="token punctuation">,</span> 0.0166 40.2%<span class="token punctuation">,</span> 0.0165 40.56%<span class="token punctuation">,</span> 0.0166 40.89%<span class="token punctuation">,</span> 0.0167 41.24%<span class="token punctuation">,</span> 0.0173 41.67%<span class="token punctuation">,</span> 0.0183 42.1%<span class="token punctuation">,</span> 0.02 42.51%<span class="token punctuation">,</span> 0.0223 42.93%<span class="token punctuation">,</span> 0.0251 43.33%<span class="token punctuation">,</span> 0.0276 43.66%<span class="token punctuation">,</span> 0.0297 43.94%<span class="token punctuation">,</span> 0.0314 44.19%<span class="token punctuation">,</span> 0.0326 44.41%<span class="token punctuation">,</span> 0.0334 44.62%<span class="token punctuation">,</span> 0.0338 44.82%<span class="token punctuation">,</span> 0.0342 45.15%<span class="token punctuation">,</span> 0.035 45.34%<span class="token punctuation">,</span> 0.0364 45.51%<span class="token punctuation">,</span> 0.0385 45.69%<span class="token punctuation">,</span> 0.0411 45.86%<span class="token punctuation">,</span> 0.044 46.01%<span class="token punctuation">,</span> 0.0472 46.17%<span class="token punctuation">,</span> 0.0506 46.32%<span class="token punctuation">,</span> 0.0545 46.48%<span class="token punctuation">,</span> 0.0597 46.67%<span class="token punctuation">,</span> 0.0665 46.93%<span class="token punctuation">,</span> 0.0706 47.1%<span class="token punctuation">,</span> 0.0742 47.27%<span class="token punctuation">,</span> 0.0775 47.45%<span class="token punctuation">,</span> 0.0807 47.66%<span class="token punctuation">,</span> 0.0832 47.86%<span class="token punctuation">,</span> 0.0851 48.07%<span class="token punctuation">,</span> 0.0864 48.27%<span class="token punctuation">,</span> 0.087 48.49%<span class="token punctuation">,</span> 0.087 48.73%<span class="token punctuation">,</span> 0.0862 49.13%<span class="token punctuation">,</span> 0.0854 49.44%<span class="token punctuation">,</span> 0.0846 49.82%<span class="token punctuation">,</span> 0.0831 50.44%<span class="token punctuation">,</span> 0.0809 51.15%<span class="token punctuation">,</span> 0.0791 51.62%<span class="token punctuation">,</span> 0.0773 51.95%<span class="token punctuation">,</span> 0.0752 52.22%<span class="token punctuation">,</span> 0.0727 52.47%<span class="token punctuation">,</span> 0.0696 52.71%<span class="token punctuation">,</span> 0.0664 52.92%<span class="token punctuation">,</span> 0.0635 53.08%<span class="token punctuation">,</span> 0.0606 53.2%<span class="token punctuation">,</span> 0.0579 53.3%<span class="token punctuation">,</span> 0.0548 53.4%<span class="token punctuation">,</span> 0.0491 53.57%<span class="token punctuation">,</span> 0.0463 53.66%<span class="token punctuation">,</span> 0.0435 53.77%<span class="token punctuation">,</span> 0.0407 53.91%<span class="token punctuation">,</span> 0.0376 54.09%<span class="token punctuation">,</span> 0.0346 54.3%<span class="token punctuation">,</span> 0.0319 54.54%<span class="token punctuation">,</span> 0.0298 54.78%<span class="token punctuation">,</span> 0.0282 55.05%<span class="token punctuation">,</span> 0.0266 55.46%<span class="token punctuation">,</span> 0.0253 55.89%<span class="token punctuation">,</span> 0.0229 56.82%<span class="token punctuation">,</span> 0.0219 57.25%<span class="token punctuation">,</span> 0.0211 57.57%<span class="token punctuation">,</span> 0.0202 57.87%<span class="token punctuation">,</span> 0.0195 58.2%<span class="token punctuation">,</span> 0.0195 58.42%<span class="token punctuation">,</span> 0.0202 58.62%<span class="token punctuation">,</span> 0.0215 58.83%<span class="token punctuation">,</span> 0.0235 59.02%<span class="token punctuation">,</span> 0.0261 59.23%<span class="token punctuation">,</span> 0.0293 59.42%<span class="token punctuation">,</span> 0.0326 59.6%<span class="token punctuation">,</span> 0.0361 59.77%<span class="token punctuation">,</span> 0.04 59.92%<span class="token punctuation">,</span> 0.0452 60.12%<span class="token punctuation">,</span> 0.0518 60.36%<span class="token punctuation">,</span> 0.0558 60.51%<span class="token punctuation">,</span> 0.0593 60.66%<span class="token punctuation">,</span> 0.0626 60.81%<span class="token punctuation">,</span> 0.0656 60.97%<span class="token punctuation">,</span> 0.0684 61.14%<span class="token punctuation">,</span> 0.0708 61.31%<span class="token punctuation">,</span> 0.0724 61.49%<span class="token punctuation">,</span> 0.0734 61.65%<span class="token punctuation">,</span> 0.0738 61.84%<span class="token punctuation">,</span> 0.0738 62.16%<span class="token punctuation">,</span> 0.074 62.36%<span class="token punctuation">,</span> 0.0747 62.58%<span class="token punctuation">,</span> 0.0759 62.82%<span class="token punctuation">,</span> 0.0776 63.09%<span class="token punctuation">,</span> 0.0797 63.4%<span class="token punctuation">,</span> 0.0823 63.79%<span class="token punctuation">,</span> 0.0853 64.3%<span class="token punctuation">,</span> 0.0875 64.79%<span class="token punctuation">,</span> 0.0891 65.24%<span class="token punctuation">,</span> 0.0901 65.59%<span class="token punctuation">,</span> 0.0911 65.89%<span class="token punctuation">,</span> 0.092 66.19%<span class="token punctuation">,</span> 0.0928 66.54%<span class="token punctuation">,</span> 0.0928 66.77%<span class="token punctuation">,</span> 0.0921 66.98%<span class="token punctuation">,</span> 0.0908 67.19%<span class="token punctuation">,</span> 0.0888 67.4%<span class="token punctuation">,</span> 0.0862 67.62%<span class="token punctuation">,</span> 0.0832 67.81%<span class="token punctuation">,</span> 0.0801 67.98%<span class="token punctuation">,</span> 0.0771 68.11%<span class="token punctuation">,</span> 0.0741 68.22%<span class="token punctuation">,</span> 0.0667 68.46%<span class="token punctuation">,</span> 0.0636 68.57%<span class="token punctuation">,</span> 0.0606 68.68%<span class="token punctuation">,</span> 0.0576 68.82%<span class="token punctuation">,</span> 0.0545 68.96%<span class="token punctuation">,</span> 0.0513 69.11%<span class="token punctuation">,</span> 0.0482 69.26%<span class="token punctuation">,</span> 0.045 69.41%<span class="token punctuation">,</span> 0.0417 69.55%<span class="token punctuation">,</span> 0.0382 69.7%<span class="token punctuation">,</span> 0.0345 69.85%<span class="token punctuation">,</span> 0.0305 70.02%<span class="token punctuation">,</span> 0.0259 70.2%<span class="token punctuation">,</span> 0.0205 70.43%<span class="token punctuation">,</span> 0.0148 70.69%<span class="token punctuation">,</span> 0.0105 70.91%<span class="token punctuation">,</span> 0.0071 71.12%<span class="token punctuation">,</span> 0.0045 71.33%<span class="token punctuation">,</span> 0.0025 71.53%<span class="token punctuation">,</span> 0.0011 71.74%<span class="token punctuation">,</span> 0.0002 71.96%<span class="token punctuation">,</span> 0 72.2%<span class="token punctuation">,</span> 0.0004 72.47%<span class="token punctuation">,</span> 0.0015 72.77%<span class="token punctuation">,</span> 0.0034 73.08%<span class="token punctuation">,</span> 0.0058 73.39%<span class="token punctuation">,</span> 0.0087 73.67%<span class="token punctuation">,</span> 0.0119 73.93%<span class="token punctuation">,</span> 0.0159 74.19%<span class="token punctuation">,</span> 0.0209 74.5%<span class="token punctuation">,</span> 0.0246 74.71%<span class="token punctuation">,</span> 0.028 74.91%<span class="token punctuation">,</span> 0.0313 75.1%<span class="token punctuation">,</span> 0.0346 75.28%<span class="token punctuation">,</span> 0.0377 75.45%<span class="token punctuation">,</span> 0.0408 75.61%<span class="token punctuation">,</span> 0.0439 75.78%<span class="token punctuation">,</span> 0.0469 75.94%<span class="token punctuation">,</span> 0.0499 76.11%<span class="token punctuation">,</span> 0.0527 76.29%<span class="token punctuation">,</span> 0.055 76.47%<span class="token punctuation">,</span> 0.0564 76.63%<span class="token punctuation">,</span> 0.0571 76.78%<span class="token punctuation">,</span> 0.0571 76.91%<span class="token punctuation">,</span> 0.0565 77.04%<span class="token punctuation">,</span> 0.0549 77.2%<span class="token punctuation">,</span> 0.0534 77.36%<span class="token punctuation">,</span> 0.0527 77.49%<span class="token punctuation">,</span> 0.0527 77.63%<span class="token punctuation">,</span> 0.0533 77.78%<span class="token punctuation">,</span> 0.0546 77.95%<span class="token punctuation">,</span> 0.0568 78.13%<span class="token punctuation">,</span> 0.0596 78.32%<span class="token punctuation">,</span> 0.0626 78.5%<span class="token punctuation">,</span> 0.0656 78.68%<span class="token punctuation">,</span> 0.0687 78.86%<span class="token punctuation">,</span> 0.0718 79.04%<span class="token punctuation">,</span> 0.075 79.23%<span class="token punctuation">,</span> 0.0782 79.44%<span class="token punctuation">,</span> 0.0816 79.67%<span class="token punctuation">,</span> 0.0851 79.93%<span class="token punctuation">,</span> 0.0889 80.25%<span class="token punctuation">,</span> 0.0926 80.6%<span class="token punctuation">,</span> 0.0955 80.94%<span class="token punctuation">,</span> 0.0976 81.26%<span class="token punctuation">,</span> 0.0992 81.6%<span class="token punctuation">,</span> 0.1002 81.96%<span class="token punctuation">,</span> 0.1005 82.31%<span class="token punctuation">,</span> 0.1002 82.64%<span class="token punctuation">,</span> 0.0993 82.94%<span class="token punctuation">,</span> 0.0979 83.22%<span class="token punctuation">,</span> 0.0959 83.49%<span class="token punctuation">,</span> 0.0934 83.74%<span class="token punctuation">,</span> 0.0903 83.99%<span class="token punctuation">,</span> 0.0866 84.23%<span class="token punctuation">,</span> 0.0831 84.43%<span class="token punctuation">,</span> 0.0795 84.61%<span class="token punctuation">,</span> 0.0758 84.77%<span class="token punctuation">,</span> 0.0719 84.92%<span class="token punctuation">,</span> 0.0619 85.27%<span class="token punctuation">,</span> 0.058 85.41%<span class="token punctuation">,</span> 0.0546 85.55%<span class="token punctuation">,</span> 0.0513 85.69%<span class="token punctuation">,</span> 0.0481 85.83%<span class="token punctuation">,</span> 0.0451 85.98%<span class="token punctuation">,</span> 0.0423 86.13%<span class="token punctuation">,</span> 0.0398 86.3%<span class="token punctuation">,</span> 0.0379 86.47%<span class="token punctuation">,</span> 0.0367 86.62%<span class="token punctuation">,</span> 0.0362 86.77%<span class="token punctuation">,</span> 0.0364 86.93%<span class="token punctuation">,</span> 0.0375 87.22%<span class="token punctuation">,</span> 0.0377 87.39%<span class="token punctuation">,</span> 0.0374 87.57%<span class="token punctuation">,</span> 0.0364 87.78%<span class="token punctuation">,</span> 0.0348 88.02%<span class="token punctuation">,</span> 0.0325 88.28%<span class="token punctuation">,</span> 0.0298 88.55%<span class="token punctuation">,</span> 0.0268 88.86%<span class="token punctuation">,</span> 0.0234 89.23%<span class="token punctuation">,</span> 0.0201 89.64%<span class="token punctuation">,</span> 0.0175 90.06%<span class="token punctuation">,</span> 0.0152 90.53%<span class="token punctuation">,</span> 0.0138 90.9%<span class="token punctuation">,</span> 0.0126 91.22%<span class="token punctuation">,</span> 0.0115 91.51%<span class="token punctuation">,</span> 0.0103 91.84%<span class="token punctuation">,</span> 0.0094 92.15%<span class="token punctuation">,</span> 0.0094 92.38%<span class="token punctuation">,</span> 0.01 92.59%<span class="token punctuation">,</span> 0.0112 92.81%<span class="token punctuation">,</span> 0.0132 93.04%<span class="token punctuation">,</span> 0.0158 93.27%<span class="token punctuation">,</span> 0.0188 93.48%<span class="token punctuation">,</span> 0.022 93.66%<span class="token punctuation">,</span> 0.0251 93.81%<span class="token punctuation">,</span> 0.0283 93.93%<span class="token punctuation">,</span> 0.0361 94.19%<span class="token punctuation">,</span> 0.0393 94.31%<span class="token punctuation">,</span> 0.0423 94.43%<span class="token punctuation">,</span> 0.0454 94.57%<span class="token punctuation">,</span> 0.0485 94.71%<span class="token punctuation">,</span> 0.0517 94.85%<span class="token punctuation">,</span> 0.0548 95%<span class="token punctuation">,</span> 0.058 95.14%<span class="token punctuation">,</span> 0.0612 95.27%<span class="token punctuation">,</span> 0.0646 95.41%<span class="token punctuation">,</span> 0.0682 95.56%<span class="token punctuation">,</span> 0.0719 95.71%<span class="token punctuation">,</span> 0.0761 95.88%<span class="token punctuation">,</span> 0.081 96.1%<span class="token punctuation">,</span> 0.0845 96.28%<span class="token punctuation">,</span> 0.0871 96.45%<span class="token punctuation">,</span> 0.0889 96.61%<span class="token punctuation">,</span> 0.0901 96.78%<span class="token punctuation">,</span> 0.0907 96.94%<span class="token punctuation">,</span> 0.0907 97.11%<span class="token punctuation">,</span> 0.09 97.28%<span class="token punctuation">,</span> 0.0886 97.44%<span class="token punctuation">,</span> 0.0868 97.6%<span class="token punctuation">,</span> 0.0845 97.74%<span class="token punctuation">,</span> 0.0798 97.96%<span class="token punctuation">,</span> 0.077 98.11%<span class="token punctuation">,</span> 0.0745 98.25%<span class="token punctuation">,</span> 0.0719 98.43%<span class="token punctuation">,</span> 0.0691 98.64%<span class="token punctuation">,</span> 0.0661 98.87%<span class="token punctuation">,</span> 0.0631 99.11%<span class="token punctuation">,</span> 0.0599 99.34%<span class="token punctuation">,</span> 0.0563 99.57%<span class="token punctuation">,</span> 0.0493 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.arm2</span> <span class="token punctuation">{</span> <span class="token property">top</span><span class="token punctuation">:</span> 110px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 110px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.1595 0%<span class="token punctuation">,</span> 0.1593 0.32%<span class="token punctuation">,</span> 0.1586 0.65%<span class="token punctuation">,</span> 0.1576 0.99%<span class="token punctuation">,</span> 0.1564 1.35%<span class="token punctuation">,</span> 0.1552 1.69%<span class="token punctuation">,</span> 0.1544 2%<span class="token punctuation">,</span> 0.1542 2.29%<span class="token punctuation">,</span> 0.1548 2.56%<span class="token punctuation">,</span> 0.1563 2.82%<span class="token punctuation">,</span> 0.1588 3.05%<span class="token punctuation">,</span> 0.1619 3.25%<span class="token punctuation">,</span> 0.1657 3.42%<span class="token punctuation">,</span> 0.1701 3.57%<span class="token punctuation">,</span> 0.1757 3.73%<span class="token punctuation">,</span> 0.1875 4.02%<span class="token punctuation">,</span> 0.1923 4.16%<span class="token punctuation">,</span> 0.196 4.29%<span class="token punctuation">,</span> 0.199 4.43%<span class="token punctuation">,</span> 0.2012 4.57%<span class="token punctuation">,</span> 0.2026 4.71%<span class="token punctuation">,</span> 0.2031 4.86%<span class="token punctuation">,</span> 0.2026 5.03%<span class="token punctuation">,</span> 0.2008 5.2%<span class="token punctuation">,</span> 0.1981 5.35%<span class="token punctuation">,</span> 0.1943 5.5%<span class="token punctuation">,</span> 0.1896 5.65%<span class="token punctuation">,</span> 0.183 5.81%<span class="token punctuation">,</span> 0.1727 6.05%<span class="token punctuation">,</span> 0.1668 6.21%<span class="token punctuation">,</span> 0.1616 6.39%<span class="token punctuation">,</span> 0.1568 6.59%<span class="token punctuation">,</span> 0.1527 6.83%<span class="token punctuation">,</span> 0.1494 7.09%<span class="token punctuation">,</span> 0.1469 7.36%<span class="token punctuation">,</span> 0.1449 7.66%<span class="token punctuation">,</span> 0.1432 8.01%<span class="token punctuation">,</span> 0.1413 8.44%<span class="token punctuation">,</span> 0.139 8.86%<span class="token punctuation">,</span> 0.1353 9.35%<span class="token punctuation">,</span> 0.1318 9.72%<span class="token punctuation">,</span> 0.1281 10.03%<span class="token punctuation">,</span> 0.124 10.32%<span class="token punctuation">,</span> 0.1187 10.66%<span class="token punctuation">,</span> 0.1132 10.97%<span class="token punctuation">,</span> 0.1088 11.2%<span class="token punctuation">,</span> 0.1045 11.41%<span class="token punctuation">,</span> 0.0999 11.63%<span class="token punctuation">,</span> 0.0947 11.87%<span class="token punctuation">,</span> 0.0888 12.11%<span class="token punctuation">,</span> 0.0825 12.34%<span class="token punctuation">,</span> 0.0758 12.54%<span class="token punctuation">,</span> 0.0693 12.71%<span class="token punctuation">,</span> 0.0627 12.85%<span class="token punctuation">,</span> 0.0542 13%<span class="token punctuation">,</span> 0.0447 13.17%<span class="token punctuation">,</span> 0.0385 13.29%<span class="token punctuation">,</span> 0.0329 13.42%<span class="token punctuation">,</span> 0.0277 13.56%<span class="token punctuation">,</span> 0.0231 13.71%<span class="token punctuation">,</span> 0.0191 13.85%<span class="token punctuation">,</span> 0.0158 13.99%<span class="token punctuation">,</span> 0.013 14.13%<span class="token punctuation">,</span> 0.0109 14.27%<span class="token punctuation">,</span> 0.0093 14.41%<span class="token punctuation">,</span> 0.0082 14.56%<span class="token punctuation">,</span> 0.0078 14.71%<span class="token punctuation">,</span> 0.0084 14.9%<span class="token punctuation">,</span> 0.0099 15.08%<span class="token punctuation">,</span> 0.0121 15.24%<span class="token punctuation">,</span> 0.0149 15.39%<span class="token punctuation">,</span> 0.0182 15.54%<span class="token punctuation">,</span> 0.0221 15.69%<span class="token punctuation">,</span> 0.0266 15.84%<span class="token punctuation">,</span> 0.0318 15.98%<span class="token punctuation">,</span> 0.0376 16.12%<span class="token punctuation">,</span> 0.044 16.24%<span class="token punctuation">,</span> 0.053 16.39%<span class="token punctuation">,</span> 0.0625 16.54%<span class="token punctuation">,</span> 0.0693 16.67%<span class="token punctuation">,</span> 0.0761 16.82%<span class="token punctuation">,</span> 0.0838 17.03%<span class="token punctuation">,</span> 0.0916 17.26%<span class="token punctuation">,</span> 0.0993 17.52%<span class="token punctuation">,</span> 0.1069 17.8%<span class="token punctuation">,</span> 0.1169 18.16%<span class="token punctuation">,</span> 0.1247 18.43%<span class="token punctuation">,</span> 0.1311 18.65%<span class="token punctuation">,</span> 0.1378 18.86%<span class="token punctuation">,</span> 0.145 19.06%<span class="token punctuation">,</span> 0.1523 19.23%<span class="token punctuation">,</span> 0.1593 19.38%<span class="token punctuation">,</span> 0.1659 19.49%<span class="token punctuation">,</span> 0.1726 19.59%<span class="token punctuation">,</span> 0.1894 19.8%<span class="token punctuation">,</span> 0.1962 19.91%<span class="token punctuation">,</span> 0.2029 20.03%<span class="token punctuation">,</span> 0.2094 20.17%<span class="token punctuation">,</span> 0.2155 20.32%<span class="token punctuation">,</span> 0.2209 20.47%<span class="token punctuation">,</span> 0.2258 20.63%<span class="token punctuation">,</span> 0.2302 20.78%<span class="token punctuation">,</span> 0.2343 20.94%<span class="token punctuation">,</span> 0.2383 21.1%<span class="token punctuation">,</span> 0.2433 21.32%<span class="token punctuation">,</span> 0.2513 21.69%<span class="token punctuation">,</span> 0.256 21.89%<span class="token punctuation">,</span> 0.2604 22.08%<span class="token punctuation">,</span> 0.265 22.26%<span class="token punctuation">,</span> 0.27 22.43%<span class="token punctuation">,</span> 0.2755 22.6%<span class="token punctuation">,</span> 0.2815 22.76%<span class="token punctuation">,</span> 0.2879 22.91%<span class="token punctuation">,</span> 0.2946 23.03%<span class="token punctuation">,</span> 0.3017 23.14%<span class="token punctuation">,</span> 0.3161 23.35%<span class="token punctuation">,</span> 0.3228 23.46%<span class="token punctuation">,</span> 0.3287 23.58%<span class="token punctuation">,</span> 0.334 23.71%<span class="token punctuation">,</span> 0.3388 23.85%<span class="token punctuation">,</span> 0.3429 23.99%<span class="token punctuation">,</span> 0.3464 24.13%<span class="token punctuation">,</span> 0.3493 24.28%<span class="token punctuation">,</span> 0.3518 24.45%<span class="token punctuation">,</span> 0.3536 24.64%<span class="token punctuation">,</span> 0.3543 24.82%<span class="token punctuation">,</span> 0.3541 24.98%<span class="token punctuation">,</span> 0.3534 25.13%<span class="token punctuation">,</span> 0.3521 25.27%<span class="token punctuation">,</span> 0.3504 25.42%<span class="token punctuation">,</span> 0.3481 25.58%<span class="token punctuation">,</span> 0.3452 25.75%<span class="token punctuation">,</span> 0.3418 25.93%<span class="token punctuation">,</span> 0.3376 26.13%<span class="token punctuation">,</span> 0.3326 26.33%<span class="token punctuation">,</span> 0.327 26.55%<span class="token punctuation">,</span> 0.3207 26.76%<span class="token punctuation">,</span> 0.3134 26.98%<span class="token punctuation">,</span> 0.3028 27.29%<span class="token punctuation">,</span> 0.2962 27.51%<span class="token punctuation">,</span> 0.2903 27.74%<span class="token punctuation">,</span> 0.2848 27.98%<span class="token punctuation">,</span> 0.28 28.22%<span class="token punctuation">,</span> 0.2758 28.45%<span class="token punctuation">,</span> 0.2722 28.67%<span class="token punctuation">,</span> 0.2692 28.87%<span class="token punctuation">,</span> 0.2667 29.07%<span class="token punctuation">,</span> 0.2647 29.26%<span class="token punctuation">,</span> 0.2632 29.46%<span class="token punctuation">,</span> 0.2624 29.68%<span class="token punctuation">,</span> 0.2626 29.9%<span class="token punctuation">,</span> 0.2637 30.08%<span class="token punctuation">,</span> 0.2654 30.24%<span class="token punctuation">,</span> 0.2676 30.39%<span class="token punctuation">,</span> 0.2705 30.53%<span class="token punctuation">,</span> 0.2739 30.67%<span class="token punctuation">,</span> 0.2779 30.81%<span class="token punctuation">,</span> 0.2825 30.94%<span class="token punctuation">,</span> 0.2875 31.06%<span class="token punctuation">,</span> 0.293 31.17%<span class="token punctuation">,</span> 0.299 31.27%<span class="token punctuation">,</span> 0.3076 31.4%<span class="token punctuation">,</span> 0.3165 31.53%<span class="token punctuation">,</span> 0.3226 31.64%<span class="token punctuation">,</span> 0.3282 31.75%<span class="token punctuation">,</span> 0.3336 31.88%<span class="token punctuation">,</span> 0.3387 32.03%<span class="token punctuation">,</span> 0.3434 32.2%<span class="token punctuation">,</span> 0.3476 32.38%<span class="token punctuation">,</span> 0.3513 32.57%<span class="token punctuation">,</span> 0.3546 32.77%<span class="token punctuation">,</span> 0.3574 33.01%<span class="token punctuation">,</span> 0.36 33.3%<span class="token punctuation">,</span> 0.3623 33.71%<span class="token punctuation">,</span> 0.3638 34.16%<span class="token punctuation">,</span> 0.3667 35.19%<span class="token punctuation">,</span> 0.3683 35.57%<span class="token punctuation">,</span> 0.3701 35.89%<span class="token punctuation">,</span> 0.3723 36.2%<span class="token punctuation">,</span> 0.3758 36.54%<span class="token punctuation">,</span> 0.3789 36.77%<span class="token punctuation">,</span> 0.3819 36.96%<span class="token punctuation">,</span> 0.3853 37.12%<span class="token punctuation">,</span> 0.3889 37.28%<span class="token punctuation">,</span> 0.3932 37.44%<span class="token punctuation">,</span> 0.398 37.6%<span class="token punctuation">,</span> 0.4034 37.76%<span class="token punctuation">,</span> 0.4092 37.9%<span class="token punctuation">,</span> 0.415 38.03%<span class="token punctuation">,</span> 0.4211 38.13%<span class="token punctuation">,</span> 0.4277 38.24%<span class="token punctuation">,</span> 0.4439 38.46%<span class="token punctuation">,</span> 0.4504 38.56%<span class="token punctuation">,</span> 0.4568 38.68%<span class="token punctuation">,</span> 0.4636 38.84%<span class="token punctuation">,</span> 0.4707 39.03%<span class="token punctuation">,</span> 0.4775 39.25%<span class="token punctuation">,</span> 0.4836 39.47%<span class="token punctuation">,</span> 0.4891 39.69%<span class="token punctuation">,</span> 0.4944 39.92%<span class="token punctuation">,</span> 0.5004 40.2%<span class="token punctuation">,</span> 0.5074 40.56%<span class="token punctuation">,</span> 0.5128 40.89%<span class="token punctuation">,</span> 0.5179 41.24%<span class="token punctuation">,</span> 0.523 41.67%<span class="token punctuation">,</span> 0.5273 42.1%<span class="token punctuation">,</span> 0.5304 42.51%<span class="token punctuation">,</span> 0.533 42.93%<span class="token punctuation">,</span> 0.5355 43.33%<span class="token punctuation">,</span> 0.5378 43.66%<span class="token punctuation">,</span> 0.5405 43.94%<span class="token punctuation">,</span> 0.5436 44.19%<span class="token punctuation">,</span> 0.5474 44.41%<span class="token punctuation">,</span> 0.5518 44.62%<span class="token punctuation">,</span> 0.5573 44.82%<span class="token punctuation">,</span> 0.567 45.15%<span class="token punctuation">,</span> 0.5715 45.34%<span class="token punctuation">,</span> 0.5745 45.51%<span class="token punctuation">,</span> 0.5764 45.69%<span class="token punctuation">,</span> 0.5771 45.86%<span class="token punctuation">,</span> 0.5766 46.01%<span class="token punctuation">,</span> 0.5752 46.17%<span class="token punctuation">,</span> 0.5728 46.32%<span class="token punctuation">,</span> 0.5693 46.48%<span class="token punctuation">,</span> 0.5638 46.67%<span class="token punctuation">,</span> 0.5565 46.93%<span class="token punctuation">,</span> 0.5528 47.1%<span class="token punctuation">,</span> 0.5501 47.27%<span class="token punctuation">,</span> 0.5484 47.45%<span class="token punctuation">,</span> 0.5477 47.66%<span class="token punctuation">,</span> 0.5483 47.86%<span class="token punctuation">,</span> 0.55 48.07%<span class="token punctuation">,</span> 0.5526 48.27%<span class="token punctuation">,</span> 0.5564 48.49%<span class="token punctuation">,</span> 0.5616 48.73%<span class="token punctuation">,</span> 0.5711 49.13%<span class="token punctuation">,</span> 0.578 49.44%<span class="token punctuation">,</span> 0.5855 49.82%<span class="token punctuation">,</span> 0.5969 50.44%<span class="token punctuation">,</span> 0.6105 51.15%<span class="token punctuation">,</span> 0.6204 51.62%<span class="token punctuation">,</span> 0.6283 51.95%<span class="token punctuation">,</span> 0.6351 52.22%<span class="token punctuation">,</span> 0.6423 52.47%<span class="token punctuation">,</span> 0.6499 52.71%<span class="token punctuation">,</span> 0.6577 52.92%<span class="token punctuation">,</span> 0.6646 53.08%<span class="token punctuation">,</span> 0.6712 53.2%<span class="token punctuation">,</span> 0.6776 53.3%<span class="token punctuation">,</span> 0.6848 53.4%<span class="token punctuation">,</span> 0.698 53.57%<span class="token punctuation">,</span> 0.7046 53.66%<span class="token punctuation">,</span> 0.711 53.77%<span class="token punctuation">,</span> 0.7177 53.91%<span class="token punctuation">,</span> 0.725 54.09%<span class="token punctuation">,</span> 0.7326 54.3%<span class="token punctuation">,</span> 0.7398 54.54%<span class="token punctuation">,</span> 0.7465 54.78%<span class="token punctuation">,</span> 0.7532 55.05%<span class="token punctuation">,</span> 0.7626 55.46%<span class="token punctuation">,</span> 0.7715 55.89%<span class="token punctuation">,</span> 0.7892 56.82%<span class="token punctuation">,</span> 0.7976 57.25%<span class="token punctuation">,</span> 0.8046 57.57%<span class="token punctuation">,</span> 0.8117 57.87%<span class="token punctuation">,</span> 0.8201 58.2%<span class="token punctuation">,</span> 0.825 58.42%<span class="token punctuation">,</span> 0.8285 58.62%<span class="token punctuation">,</span> 0.8311 58.83%<span class="token punctuation">,</span> 0.8327 59.02%<span class="token punctuation">,</span> 0.8331 59.23%<span class="token punctuation">,</span> 0.8324 59.42%<span class="token punctuation">,</span> 0.8306 59.6%<span class="token punctuation">,</span> 0.8279 59.77%<span class="token punctuation">,</span> 0.8242 59.92%<span class="token punctuation">,</span> 0.8185 60.12%<span class="token punctuation">,</span> 0.811 60.36%<span class="token punctuation">,</span> 0.8072 60.51%<span class="token punctuation">,</span> 0.8043 60.66%<span class="token punctuation">,</span> 0.8024 60.81%<span class="token punctuation">,</span> 0.8015 60.97%<span class="token punctuation">,</span> 0.8016 61.14%<span class="token punctuation">,</span> 0.803 61.31%<span class="token punctuation">,</span> 0.8055 61.49%<span class="token punctuation">,</span> 0.8092 61.65%<span class="token punctuation">,</span> 0.8144 61.84%<span class="token punctuation">,</span> 0.8243 62.16%<span class="token punctuation">,</span> 0.8297 62.36%<span class="token punctuation">,</span> 0.8342 62.58%<span class="token punctuation">,</span> 0.8383 62.82%<span class="token punctuation">,</span> 0.8419 63.09%<span class="token punctuation">,</span> 0.8451 63.4%<span class="token punctuation">,</span> 0.8484 63.79%<span class="token punctuation">,</span> 0.8524 64.3%<span class="token punctuation">,</span> 0.8569 64.79%<span class="token punctuation">,</span> 0.8619 65.24%<span class="token punctuation">,</span> 0.8665 65.59%<span class="token punctuation">,</span> 0.8711 65.89%<span class="token punctuation">,</span> 0.8764 66.19%<span class="token punctuation">,</span> 0.8831 66.54%<span class="token punctuation">,</span> 0.8881 66.77%<span class="token punctuation">,</span> 0.8929 66.98%<span class="token punctuation">,</span> 0.898 67.19%<span class="token punctuation">,</span> 0.9038 67.4%<span class="token punctuation">,</span> 0.9102 67.62%<span class="token punctuation">,</span> 0.9169 67.81%<span class="token punctuation">,</span> 0.9238 67.98%<span class="token punctuation">,</span> 0.9302 68.11%<span class="token punctuation">,</span> 0.9369 68.22%<span class="token punctuation">,</span> 0.953 68.46%<span class="token punctuation">,</span> 0.9594 68.57%<span class="token punctuation">,</span> 0.9653 68.68%<span class="token punctuation">,</span> 0.9711 68.82%<span class="token punctuation">,</span> 0.9763 68.96%<span class="token punctuation">,</span> 0.9812 69.11%<span class="token punctuation">,</span> 0.9854 69.26%<span class="token punctuation">,</span> 0.989 69.41%<span class="token punctuation">,</span> 0.992 69.55%<span class="token punctuation">,</span> 0.9945 69.7%<span class="token punctuation">,</span> 0.9966 69.85%<span class="token punctuation">,</span> 0.9983 70.02%<span class="token punctuation">,</span> 0.9995 70.2%<span class="token punctuation">,</span> 1 70.43%<span class="token punctuation">,</span> 0.9995 70.69%<span class="token punctuation">,</span> 0.9983 70.91%<span class="token punctuation">,</span> 0.9966 71.12%<span class="token punctuation">,</span> 0.9945 71.33%<span class="token punctuation">,</span> 0.9921 71.53%<span class="token punctuation">,</span> 0.9893 71.74%<span class="token punctuation">,</span> 0.9862 71.96%<span class="token punctuation">,</span> 0.9826 72.2%<span class="token punctuation">,</span> 0.9785 72.47%<span class="token punctuation">,</span> 0.9739 72.77%<span class="token punctuation">,</span> 0.9687 73.08%<span class="token punctuation">,</span> 0.9632 73.39%<span class="token punctuation">,</span> 0.9575 73.67%<span class="token punctuation">,</span> 0.9515 73.93%<span class="token punctuation">,</span> 0.9443 74.19%<span class="token punctuation">,</span> 0.9356 74.5%<span class="token punctuation">,</span> 0.9301 74.71%<span class="token punctuation">,</span> 0.9255 74.91%<span class="token punctuation">,</span> 0.9216 75.1%<span class="token punctuation">,</span> 0.9185 75.28%<span class="token punctuation">,</span> 0.9161 75.45%<span class="token punctuation">,</span> 0.9144 75.61%<span class="token punctuation">,</span> 0.9133 75.78%<span class="token punctuation">,</span> 0.9129 75.94%<span class="token punctuation">,</span> 0.9133 76.11%<span class="token punctuation">,</span> 0.9147 76.29%<span class="token punctuation">,</span> 0.9173 76.47%<span class="token punctuation">,</span> 0.9207 76.63%<span class="token punctuation">,</span> 0.9249 76.78%<span class="token punctuation">,</span> 0.9298 76.91%<span class="token punctuation">,</span> 0.9356 77.04%<span class="token punctuation">,</span> 0.9444 77.2%<span class="token punctuation">,</span> 0.9532 77.36%<span class="token punctuation">,</span> 0.9591 77.49%<span class="token punctuation">,</span> 0.9641 77.63%<span class="token punctuation">,</span> 0.9685 77.78%<span class="token punctuation">,</span> 0.9721 77.95%<span class="token punctuation">,</span> 0.9749 78.13%<span class="token punctuation">,</span> 0.9767 78.32%<span class="token punctuation">,</span> 0.9774 78.5%<span class="token punctuation">,</span> 0.9773 78.68%<span class="token punctuation">,</span> 0.9765 78.86%<span class="token punctuation">,</span> 0.9751 79.04%<span class="token punctuation">,</span> 0.9731 79.23%<span class="token punctuation">,</span> 0.9705 79.44%<span class="token punctuation">,</span> 0.9671 79.67%<span class="token punctuation">,</span> 0.9629 79.93%<span class="token punctuation">,</span> 0.9575 80.25%<span class="token punctuation">,</span> 0.9513 80.6%<span class="token punctuation">,</span> 0.9461 80.94%<span class="token punctuation">,</span> 0.9415 81.26%<span class="token punctuation">,</span> 0.9374 81.6%<span class="token punctuation">,</span> 0.9335 81.96%<span class="token punctuation">,</span> 0.93 82.31%<span class="token punctuation">,</span> 0.9269 82.64%<span class="token punctuation">,</span> 0.9245 82.94%<span class="token punctuation">,</span> 0.9227 83.22%<span class="token punctuation">,</span> 0.9216 83.49%<span class="token punctuation">,</span> 0.9212 83.74%<span class="token punctuation">,</span> 0.9218 83.99%<span class="token punctuation">,</span> 0.9235 84.23%<span class="token punctuation">,</span> 0.926 84.43%<span class="token punctuation">,</span> 0.9293 84.61%<span class="token punctuation">,</span> 0.9333 84.77%<span class="token punctuation">,</span> 0.9382 84.92%<span class="token punctuation">,</span> 0.9515 85.27%<span class="token punctuation">,</span> 0.956 85.41%<span class="token punctuation">,</span> 0.9596 85.55%<span class="token punctuation">,</span> 0.9623 85.69%<span class="token punctuation">,</span> 0.9642 85.83%<span class="token punctuation">,</span> 0.9652 85.98%<span class="token punctuation">,</span> 0.9653 86.13%<span class="token punctuation">,</span> 0.9642 86.3%<span class="token punctuation">,</span> 0.962 86.47%<span class="token punctuation">,</span> 0.9587 86.62%<span class="token punctuation">,</span> 0.9545 86.77%<span class="token punctuation">,</span> 0.9488 86.93%<span class="token punctuation">,</span> 0.9369 87.22%<span class="token punctuation">,</span> 0.9311 87.39%<span class="token punctuation">,</span> 0.9261 87.57%<span class="token punctuation">,</span> 0.9216 87.78%<span class="token punctuation">,</span> 0.9178 88.02%<span class="token punctuation">,</span> 0.9147 88.28%<span class="token punctuation">,</span> 0.9124 88.55%<span class="token punctuation">,</span> 0.9105 88.86%<span class="token punctuation">,</span> 0.9088 89.23%<span class="token punctuation">,</span> 0.9068 89.64%<span class="token punctuation">,</span> 0.9045 90.06%<span class="token punctuation">,</span> 0.9008 90.53%<span class="token punctuation">,</span> 0.8969 90.9%<span class="token punctuation">,</span> 0.893 91.22%<span class="token punctuation">,</span> 0.8887 91.51%<span class="token punctuation">,</span> 0.8833 91.84%<span class="token punctuation">,</span> 0.8775 92.15%<span class="token punctuation">,</span> 0.8728 92.38%<span class="token punctuation">,</span> 0.8683 92.59%<span class="token punctuation">,</span> 0.8633 92.81%<span class="token punctuation">,</span> 0.8578 93.04%<span class="token punctuation">,</span> 0.8516 93.27%<span class="token punctuation">,</span> 0.845 93.48%<span class="token punctuation">,</span> 0.8382 93.66%<span class="token punctuation">,</span> 0.8317 93.81%<span class="token punctuation">,</span> 0.825 93.93%<span class="token punctuation">,</span> 0.8086 94.19%<span class="token punctuation">,</span> 0.8022 94.31%<span class="token punctuation">,</span> 0.7965 94.43%<span class="token punctuation">,</span> 0.7911 94.57%<span class="token punctuation">,</span> 0.7862 94.71%<span class="token punctuation">,</span> 0.7819 94.85%<span class="token punctuation">,</span> 0.7782 95%<span class="token punctuation">,</span> 0.7752 95.14%<span class="token punctuation">,</span> 0.7727 95.27%<span class="token punctuation">,</span> 0.7707 95.41%<span class="token punctuation">,</span> 0.7692 95.56%<span class="token punctuation">,</span> 0.7683 95.71%<span class="token punctuation">,</span> 0.768 95.88%<span class="token punctuation">,</span> 0.7688 96.1%<span class="token punctuation">,</span> 0.7705 96.28%<span class="token punctuation">,</span> 0.7726 96.45%<span class="token punctuation">,</span> 0.7753 96.61%<span class="token punctuation">,</span> 0.7784 96.78%<span class="token punctuation">,</span> 0.7821 96.94%<span class="token punctuation">,</span> 0.7864 97.11%<span class="token punctuation">,</span> 0.7913 97.28%<span class="token punctuation">,</span> 0.7969 97.44%<span class="token punctuation">,</span> 0.803 97.6%<span class="token punctuation">,</span> 0.8098 97.74%<span class="token punctuation">,</span> 0.8222 97.96%<span class="token punctuation">,</span> 0.8296 98.11%<span class="token punctuation">,</span> 0.8362 98.25%<span class="token punctuation">,</span> 0.843 98.43%<span class="token punctuation">,</span> 0.8499 98.64%<span class="token punctuation">,</span> 0.8569 98.87%<span class="token punctuation">,</span> 0.8635 99.11%<span class="token punctuation">,</span> 0.8694 99.34%<span class="token punctuation">,</span> 0.8751 99.57%<span class="token punctuation">,</span> 0.885 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <pre><code><span class="token atrule"><span class="token rule">@scope</span></span> <span class="token punctuation">{</span> <span class="token selector">:scope</span> <span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> 400px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 400px<span class="token punctuation">;</span> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span> <span class="token property">background</span><span class="token punctuation">:</span> #cbeefa<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 8px<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> ball-x</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> 55px 0 <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> 347.32px 0 <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> ball-y</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>157.1px<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>347.68px<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> ball-r</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> <span class="token property">rotate</span><span class="token punctuation">:</span> -21.04rad <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> <span class="token property">rotate</span><span class="token punctuation">:</span> 24.87rad <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.ball</span> <span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span> <span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> 32px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 32px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.line</span> <span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span> <span class="token property">left</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>50% - 1px<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">background</span><span class="token punctuation">:</span> #0003<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.ball-0</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #e74c3c<span class="token punctuation">;</span> <span class="token property">animation</span><span class="token punctuation">:</span> ball-x 3s infinite <span class="token function">linear</span><span class="token punctuation">(</span>0.2497 0%<span class="token punctuation">,</span> 0.2568 2.08%<span class="token punctuation">,</span> 0.1618 8.75%<span class="token punctuation">,</span> 0.1104 12.36%<span class="token punctuation">,</span> 0.0668 15.42%<span class="token punctuation">,</span> 0.0431 17.08%<span class="token punctuation">,</span> 0.0154 19.03%<span class="token punctuation">,</span> 0 20.69%<span class="token punctuation">,</span> 0.0257 22.08%<span class="token punctuation">,</span> 0.1412 28.33%<span class="token punctuation">,</span> 0.2482 34.31%<span class="token punctuation">,</span> 0.2094 40.97%<span class="token punctuation">,</span> 0.1697 47.78%<span class="token punctuation">,</span> 0.1427 50.83%<span class="token punctuation">,</span> 0.0078 55.28%<span class="token punctuation">,</span> 0.0041 56.67%<span class="token punctuation">,</span> 0.1008 61.53%<span class="token punctuation">,</span> 0.1378 63.61%<span class="token punctuation">,</span> 0.2337 70.28%<span class="token punctuation">,</span> 0.2823 76.94%<span class="token punctuation">,</span> 0.2974 79.72%<span class="token punctuation">,</span> 0.3338 86.39%<span class="token punctuation">,</span> 0.357 91.39%<span class="token punctuation">,</span> 0.312 98.06%<span class="token punctuation">,</span> 0.3023 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> ball-y 3s infinite <span class="token function">linear</span><span class="token punctuation">(</span>0.0152 0%<span class="token punctuation">,</span> 0 2.08%<span class="token punctuation">,</span> 0.0412 8.75%<span class="token punctuation">,</span> 0.1337 12.36%<span class="token punctuation">,</span> 0.2504 15.42%<span class="token punctuation">,</span> 0.3289 17.08%<span class="token punctuation">,</span> 0.4337 19.03%<span class="token punctuation">,</span> 0.4083 20.69%<span class="token punctuation">,</span> 0.4019 22.08%<span class="token punctuation">,</span> 0.4628 28.33%<span class="token punctuation">,</span> 0.6514 34.31%<span class="token punctuation">,</span> 0.6669 40.97%<span class="token punctuation">,</span> 0.8558 47.78%<span class="token punctuation">,</span> 0.9862 50.83%<span class="token punctuation">,</span> 0.997 55.28%<span class="token punctuation">,</span> 0.9899 56.67%<span class="token punctuation">,</span> 0.9929 61.53%<span class="token punctuation">,</span> 0.9985 63.61%<span class="token punctuation">,</span> 1 70.28%<span class="token punctuation">,</span> 1 76.94%<span class="token punctuation">,</span> 1 79.72%<span class="token punctuation">,</span> 1 86.39%<span class="token punctuation">,</span> 1 91.39%<span class="token punctuation">,</span> 1 98.06%<span class="token punctuation">,</span> 1 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.line</span> <span class="token punctuation">{</span> <span class="token property">animation</span><span class="token punctuation">:</span> ball-r 3s infinite <span class="token function">linear</span><span class="token punctuation">(</span>0.4583 0%<span class="token punctuation">,</span> 0.466 2.08%<span class="token punctuation">,</span> 0.5046 8.75%<span class="token punctuation">,</span> 0.5254 12.36%<span class="token punctuation">,</span> 0.5431 15.42%<span class="token punctuation">,</span> 0.5527 17.08%<span class="token punctuation">,</span> 0.564 19.03%<span class="token punctuation">,</span> 0.564 20.69%<span class="token punctuation">,</span> 0.5596 22.08%<span class="token punctuation">,</span> 0.5395 28.33%<span class="token punctuation">,</span> 0.5199 34.31%<span class="token punctuation">,</span> 0.4818 40.97%<span class="token punctuation">,</span> 0.4429 47.78%<span class="token punctuation">,</span> 0.4298 50.83%<span class="token punctuation">,</span> 0.4529 55.28%<span class="token punctuation">,</span> 0.4529 56.67%<span class="token punctuation">,</span> 0.4551 61.53%<span class="token punctuation">,</span> 0.4608 63.61%<span class="token punctuation">,</span> 0.5027 70.28%<span class="token punctuation">,</span> 0.5287 76.94%<span class="token punctuation">,</span> 0.5368 79.72%<span class="token punctuation">,</span> 0.5562 86.39%<span class="token punctuation">,</span> 0.5704 91.39%<span class="token punctuation">,</span> 0.5653 98.06%<span class="token punctuation">,</span> 0.5602 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.ball-1</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #3498db<span class="token punctuation">;</span> <span class="token property">animation</span><span class="token punctuation">:</span> ball-x 3s infinite <span class="token function">linear</span><span class="token punctuation">(</span>0.3729 0%<span class="token punctuation">,</span> 0.3524 2.08%<span class="token punctuation">,</span> 0.4044 8.75%<span class="token punctuation">,</span> 0.4326 12.36%<span class="token punctuation">,</span> 0.4565 15.42%<span class="token punctuation">,</span> 0.4695 17.08%<span class="token punctuation">,</span> 0.4844 19.03%<span class="token punctuation">,</span> 0.4958 20.69%<span class="token punctuation">,</span> 0.5052 22.08%<span class="token punctuation">,</span> 0.5478 28.33%<span class="token punctuation">,</span> 0.5884 34.31%<span class="token punctuation">,</span> 0.6338 40.97%<span class="token punctuation">,</span> 0.6801 47.78%<span class="token punctuation">,</span> 0.701 50.83%<span class="token punctuation">,</span> 0.8078 55.28%<span class="token punctuation">,</span> 0.8437 56.67%<span class="token punctuation">,</span> 0.9671 61.53%<span class="token punctuation">,</span> 1 63.61%<span class="token punctuation">,</span> 0.9043 70.28%<span class="token punctuation">,</span> 0.8085 76.94%<span class="token punctuation">,</span> 0.7791 79.72%<span class="token punctuation">,</span> 0.857 86.39%<span class="token punctuation">,</span> 0.9165 91.39%<span class="token punctuation">,</span> 0.9962 98.06%<span class="token punctuation">,</span> 0.9743 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> ball-y 3s infinite <span class="token function">linear</span><span class="token punctuation">(</span>0.0152 0%<span class="token punctuation">,</span> 0.0536 2.08%<span class="token punctuation">,</span> 0.307 8.75%<span class="token punctuation">,</span> 0.5143 12.36%<span class="token punctuation">,</span> 0.7282 15.42%<span class="token punctuation">,</span> 0.8598 17.08%<span class="token punctuation">,</span> 0.9822 19.03%<span class="token punctuation">,</span> 0.8706 20.69%<span class="token punctuation">,</span> 0.7856 22.08%<span class="token punctuation">,</span> 0.4933 28.33%<span class="token punctuation">,</span> 0.3518 34.31%<span class="token punctuation">,</span> 0.3531 40.97%<span class="token punctuation">,</span> 0.5275 47.78%<span class="token punctuation">,</span> 0.6628 50.83%<span class="token punctuation">,</span> 0.766 55.28%<span class="token punctuation">,</span> 0.8085 56.67%<span class="token punctuation">,</span> 0.9892 61.53%<span class="token punctuation">,</span> 0.9146 63.61%<span class="token punctuation">,</span> 0.7964 70.28%<span class="token punctuation">,</span> 0.8461 76.94%<span class="token punctuation">,</span> 0.9019 79.72%<span class="token punctuation">,</span> 0.9038 86.39%<span class="token punctuation">,</span> 0.9925 91.39%<span class="token punctuation">,</span> 0.9186 98.06%<span class="token punctuation">,</span> 0.9153 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.line</span> <span class="token punctuation">{</span> <span class="token property">animation</span><span class="token punctuation">:</span> ball-r 3s infinite <span class="token function">linear</span><span class="token punctuation">(</span>0.4583 0%<span class="token punctuation">,</span> 0.4595 2.08%<span class="token punctuation">,</span> 0.4771 8.75%<span class="token punctuation">,</span> 0.4866 12.36%<span class="token punctuation">,</span> 0.4946 15.42%<span class="token punctuation">,</span> 0.499 17.08%<span class="token punctuation">,</span> 0.5044 19.03%<span class="token punctuation">,</span> 0.5106 20.69%<span class="token punctuation">,</span> 0.5157 22.08%<span class="token punctuation">,</span> 0.5386 28.33%<span class="token punctuation">,</span> 0.5606 34.31%<span class="token punctuation">,</span> 0.5851 40.97%<span class="token punctuation">,</span> 0.6102 47.78%<span class="token punctuation">,</span> 0.6214 50.83%<span class="token punctuation">,</span> 0.6281 55.28%<span class="token punctuation">,</span> 0.6299 56.67%<span class="token punctuation">,</span> 0.6384 61.53%<span class="token punctuation">,</span> 0.6585 63.61%<span class="token punctuation">,</span> 0.7299 70.28%<span class="token punctuation">,</span> 0.8014 76.94%<span class="token punctuation">,</span> 0.8299 79.72%<span class="token punctuation">,</span> 0.8974 86.39%<span class="token punctuation">,</span> 0.9469 91.39%<span class="token punctuation">,</span> 0.9951 98.06%<span class="token punctuation">,</span> 1 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.ball-2</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #2ecc71<span class="token punctuation">;</span> <span class="token property">animation</span><span class="token punctuation">:</span> ball-x 3s infinite <span class="token function">linear</span><span class="token punctuation">(</span>0.496 0%<span class="token punctuation">,</span> 0.4259 2.08%<span class="token punctuation">,</span> 0.2016 8.75%<span class="token punctuation">,</span> 0.0819 12.36%<span class="token punctuation">,</span> 0.0139 15.42%<span class="token punctuation">,</span> 0.0002 17.08%<span class="token punctuation">,</span> 0.0348 19.03%<span class="token punctuation">,</span> 0.0793 20.69%<span class="token punctuation">,</span> 0.1163 22.08%<span class="token punctuation">,</span> 0.278 28.33%<span class="token punctuation">,</span> 0.3 34.31%<span class="token punctuation">,</span> 0.4828 40.97%<span class="token punctuation">,</span> 0.6013 47.78%<span class="token punctuation">,</span> 0.6539 50.83%<span class="token punctuation">,</span> 0.6538 55.28%<span class="token punctuation">,</span> 0.6547 56.67%<span class="token punctuation">,</span> 0.67 61.53%<span class="token punctuation">,</span> 0.6766 63.61%<span class="token punctuation">,</span> 0.6977 70.28%<span class="token punctuation">,</span> 0.7187 76.94%<span class="token punctuation">,</span> 0.7173 79.72%<span class="token punctuation">,</span> 0.5867 86.39%<span class="token punctuation">,</span> 0.6124 91.39%<span class="token punctuation">,</span> 0.6259 98.06%<span class="token punctuation">,</span> 0.6284 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> ball-y 3s infinite <span class="token function">linear</span><span class="token punctuation">(</span>0.0152 0%<span class="token punctuation">,</span> 0.1441 2.08%<span class="token punctuation">,</span> 0.6666 8.75%<span class="token punctuation">,</span> 0.9873 12.36%<span class="token punctuation">,</span> 0.7535 15.42%<span class="token punctuation">,</span> 0.6471 17.08%<span class="token punctuation">,</span> 0.5508 19.03%<span class="token punctuation">,</span> 0.6103 20.69%<span class="token punctuation">,</span> 0.6678 22.08%<span class="token punctuation">,</span> 0.9894 28.33%<span class="token punctuation">,</span> 0.7495 34.31%<span class="token punctuation">,</span> 0.995 40.97%<span class="token punctuation">,</span> 0.8163 47.78%<span class="token punctuation">,</span> 0.7874 50.83%<span class="token punctuation">,</span> 0.9648 55.28%<span class="token punctuation">,</span> 0.972 56.67%<span class="token punctuation">,</span> 0.8149 61.53%<span class="token punctuation">,</span> 0.7749 63.61%<span class="token punctuation">,</span> 0.757 70.28%<span class="token punctuation">,</span> 0.907 76.94%<span class="token punctuation">,</span> 0.9958 79.72%<span class="token punctuation">,</span> 0.9999 86.39%<span class="token punctuation">,</span> 0.9999 91.39%<span class="token punctuation">,</span> 0.9999 98.06%<span class="token punctuation">,</span> 0.9999 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.line</span> <span class="token punctuation">{</span> <span class="token property">animation</span><span class="token punctuation">:</span> ball-r 3s infinite <span class="token function">linear</span><span class="token punctuation">(</span>0.4583 0%<span class="token punctuation">,</span> 0.4584 2.08%<span class="token punctuation">,</span> 0.4588 8.75%<span class="token punctuation">,</span> 0.457 12.36%<span class="token punctuation">,</span> 0.4206 15.42%<span class="token punctuation">,</span> 0.3965 17.08%<span class="token punctuation">,</span> 0.3585 19.03%<span class="token punctuation">,</span> 0.3192 20.69%<span class="token punctuation">,</span> 0.2864 22.08%<span class="token punctuation">,</span> 0.1441 28.33%<span class="token punctuation">,</span> 0.1506 34.31%<span class="token punctuation">,</span> 0.1416 40.97%<span class="token punctuation">,</span> 0.2034 47.78%<span class="token punctuation">,</span> 0.2318 50.83%<span class="token punctuation">,</span> 0.2635 55.28%<span class="token punctuation">,</span> 0.2694 56.67%<span class="token punctuation">,</span> 0.2777 61.53%<span class="token punctuation">,</span> 0.2813 63.61%<span class="token punctuation">,</span> 0.2926 70.28%<span class="token punctuation">,</span> 0.304 76.94%<span class="token punctuation">,</span> 0.3072 79.72%<span class="token punctuation">,</span> 0.2914 86.39%<span class="token punctuation">,</span> 0.2712 91.39%<span class="token punctuation">,</span> 0.2708 98.06%<span class="token punctuation">,</span> 0.2722 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.ball-3</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #f39c12<span class="token punctuation">;</span> <span class="token property">animation</span><span class="token punctuation">:</span> ball-x 3s infinite <span class="token function">linear</span><span class="token punctuation">(</span>0.6192 0%<span class="token punctuation">,</span> 0.6725 2.08%<span class="token punctuation">,</span> 0.8433 8.75%<span class="token punctuation">,</span> 0.9358 12.36%<span class="token punctuation">,</span> 0.9928 15.42%<span class="token punctuation">,</span> 0.9587 17.08%<span class="token punctuation">,</span> 0.9188 19.03%<span class="token punctuation">,</span> 0.8847 20.69%<span class="token punctuation">,</span> 0.8551 22.08%<span class="token punctuation">,</span> 0.711 28.33%<span class="token punctuation">,</span> 0.5732 34.31%<span class="token punctuation">,</span> 0.4194 40.97%<span class="token punctuation">,</span> 0.2624 47.78%<span class="token punctuation">,</span> 0.202 50.83%<span class="token punctuation">,</span> 0.225 55.28%<span class="token punctuation">,</span> 0.2331 56.67%<span class="token punctuation">,</span> 0.2616 61.53%<span class="token punctuation">,</span> 0.2737 63.61%<span class="token punctuation">,</span> 0.3116 70.28%<span class="token punctuation">,</span> 0.3998 76.94%<span class="token punctuation">,</span> 0.432 79.72%<span class="token punctuation">,</span> 0.5028 86.39%<span class="token punctuation">,</span> 0.4409 91.39%<span class="token punctuation">,</span> 0.458 98.06%<span class="token punctuation">,</span> 0.4594 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> ball-y 3s infinite <span class="token function">linear</span><span class="token punctuation">(</span>0.0152 0%<span class="token punctuation">,</span> 0.0464 2.08%<span class="token punctuation">,</span> 0.2565 8.75%<span class="token punctuation">,</span> 0.4404 12.36%<span class="token punctuation">,</span> 0.6253 15.42%<span class="token punctuation">,</span> 0.7107 17.08%<span class="token punctuation">,</span> 0.8236 19.03%<span class="token punctuation">,</span> 0.9317 20.69%<span class="token punctuation">,</span> 0.9758 22.08%<span class="token punctuation">,</span> 0.6981 28.33%<span class="token punctuation">,</span> 0.5705 34.31%<span class="token punctuation">,</span> 0.5873 40.97%<span class="token punctuation">,</span> 0.7777 47.78%<span class="token punctuation">,</span> 0.8977 50.83%<span class="token punctuation">,</span> 0.8766 55.28%<span class="token punctuation">,</span> 0.8831 56.67%<span class="token punctuation">,</span> 0.9632 61.53%<span class="token punctuation">,</span> 0.9858 63.61%<span class="token punctuation">,</span> 0.9193 70.28%<span class="token punctuation">,</span> 0.9886 76.94%<span class="token punctuation">,</span> 0.9674 79.72%<span class="token punctuation">,</span> 0.978 86.39%<span class="token punctuation">,</span> 0.985 91.39%<span class="token punctuation">,</span> 0.9881 98.06%<span class="token punctuation">,</span> 0.996 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.line</span> <span class="token punctuation">{</span> <span class="token property">animation</span><span class="token punctuation">:</span> ball-r 3s infinite <span class="token function">linear</span><span class="token punctuation">(</span>0.4583 0%<span class="token punctuation">,</span> 0.4574 2.08%<span class="token punctuation">,</span> 0.4545 8.75%<span class="token punctuation">,</span> 0.4529 12.36%<span class="token punctuation">,</span> 0.4453 15.42%<span class="token punctuation">,</span> 0.4203 17.08%<span class="token punctuation">,</span> 0.3912 19.03%<span class="token punctuation">,</span> 0.3663 20.69%<span class="token punctuation">,</span> 0.3466 22.08%<span class="token punctuation">,</span> 0.2701 28.33%<span class="token punctuation">,</span> 0.197 34.31%<span class="token punctuation">,</span> 0.1153 40.97%<span class="token punctuation">,</span> 0.032 47.78%<span class="token punctuation">,</span> 0 50.83%<span class="token punctuation">,</span> 0.0114 55.28%<span class="token punctuation">,</span> 0.0155 56.67%<span class="token punctuation">,</span> 0.0297 61.53%<span class="token punctuation">,</span> 0.036 63.61%<span class="token punctuation">,</span> 0.0566 70.28%<span class="token punctuation">,</span> 0.0644 76.94%<span class="token punctuation">,</span> 0.0775 79.72%<span class="token punctuation">,</span> 0.1155 86.39%<span class="token punctuation">,</span> 0.1171 91.39%<span class="token punctuation">,</span> 0.107 98.06%<span class="token punctuation">,</span> 0.1078 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <pre><code><span class="token atrule"><span class="token rule">@scope</span></span> <span class="token punctuation">{</span> <span class="token selector">:scope</span> <span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> 400px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 400px<span class="token punctuation">;</span> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span> <span class="token property">background</span><span class="token punctuation">:</span> #0a0a0a<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 8px<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p</span> <span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span> <span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> 5px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 5px<span class="token punctuation">;</span> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">rgba</span><span class="token punctuation">(</span>255<span class="token punctuation">,</span> 255<span class="token punctuation">,</span> 255<span class="token punctuation">,</span> 0.6<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span> <span class="token property">animation</span><span class="token punctuation">:</span> p-x 6s infinite<span class="token punctuation">,</span> p-y 6s infinite<span class="token punctuation">,</span> p-o 6s infinite<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> p-x</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> -2.28px 0 <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> 601.67px 0 <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> p-y</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>-1.44px<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>402.04px<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> p-o</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> <span class="token property">opacity</span><span class="token punctuation">:</span> 0 <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> <span class="token property">opacity</span><span class="token punctuation">:</span> 1 <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.p-0</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.6477 0%<span class="token punctuation">,</span> 0.6587 4.31%<span class="token punctuation">,</span> 0.6904 8.21%<span class="token punctuation">,</span> 0.7268 12.1%<span class="token punctuation">,</span> 0.7704 19.75%<span class="token punctuation">,</span> 0.8207 30.04%<span class="token punctuation">,</span> 0.8605 34.91%<span class="token punctuation">,</span> 0.9198 39.36%<span class="token punctuation">,</span> 0.9741 43.12%<span class="token punctuation">,</span> 0.9965 45.2%<span class="token punctuation">,</span> 0.9975 45.34%<span class="token punctuation">,</span> 0.004 45.34%<span class="token punctuation">,</span> 0.0112 48.68%<span class="token punctuation">,</span> 0.0038 50.35%<span class="token punctuation">,</span> 0.0028 50.49%<span class="token punctuation">,</span> 0.9962 50.49%<span class="token punctuation">,</span> 0.9484 54.24%<span class="token punctuation">,</span> 0.86 59.11%<span class="token punctuation">,</span> 0.7883 63%<span class="token punctuation">,</span> 0.7655 66.2%<span class="token punctuation">,</span> 0.7549 69.96%<span class="token punctuation">,</span> 0.7275 73.44%<span class="token punctuation">,</span> 0.6708 76.77%<span class="token punctuation">,</span> 0.6001 80.11%<span class="token punctuation">,</span> 0.4966 84.7%<span class="token punctuation">,</span> 0.2796 92.35%<span class="token punctuation">,</span> 0.1194 97.64%<span class="token punctuation">,</span> 0.0565 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7578 0%<span class="token punctuation">,</span> 0.8098 4.31%<span class="token punctuation">,</span> 0.8525 8.21%<span class="token punctuation">,</span> 0.8388 12.1%<span class="token punctuation">,</span> 0.7506 19.75%<span class="token punctuation">,</span> 0.5623 30.04%<span class="token punctuation">,</span> 0.4702 34.91%<span class="token punctuation">,</span> 0.4303 39.36%<span class="token punctuation">,</span> 0.4524 43.12%<span class="token punctuation">,</span> 0.4849 45.2%<span class="token punctuation">,</span> 0.4873 45.34%<span class="token punctuation">,</span> 0.4873 45.34%<span class="token punctuation">,</span> 0.5332 48.68%<span class="token punctuation">,</span> 0.5377 50.35%<span class="token punctuation">,</span> 0.5378 50.49%<span class="token punctuation">,</span> 0.5378 50.49%<span class="token punctuation">,</span> 0.5117 54.24%<span class="token punctuation">,</span> 0.4575 59.11%<span class="token punctuation">,</span> 0.4723 63%<span class="token punctuation">,</span> 0.5054 66.2%<span class="token punctuation">,</span> 0.5887 69.96%<span class="token punctuation">,</span> 0.6997 73.44%<span class="token punctuation">,</span> 0.7806 76.77%<span class="token punctuation">,</span> 0.7913 80.11%<span class="token punctuation">,</span> 0.725 84.7%<span class="token punctuation">,</span> 0.5522 92.35%<span class="token punctuation">,</span> 0.4955 97.64%<span class="token punctuation">,</span> 0.498 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.667<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-1</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.0531 0%<span class="token punctuation">,</span> 0.0066 1.95%<span class="token punctuation">,</span> 0.0034 2.09%<span class="token punctuation">,</span> 0.9968 2.09%<span class="token punctuation">,</span> 0.9424 5.7%<span class="token punctuation">,</span> 0.9178 10.15%<span class="token punctuation">,</span> 0.9155 14.74%<span class="token punctuation">,</span> 0.9335 19.19%<span class="token punctuation">,</span> 0.9781 23.37%<span class="token punctuation">,</span> 0.995 24.34%<span class="token punctuation">,</span> 0.9975 24.48%<span class="token punctuation">,</span> 0.004 24.48%<span class="token punctuation">,</span> 0.0739 28.51%<span class="token punctuation">,</span> 0.159 32.41%<span class="token punctuation">,</span> 0.3154 38.11%<span class="token punctuation">,</span> 0.4249 42.42%<span class="token punctuation">,</span> 0.5269 46.87%<span class="token punctuation">,</span> 0.5808 50.49%<span class="token punctuation">,</span> 0.601 54.52%<span class="token punctuation">,</span> 0.6027 55.91%<span class="token punctuation">,</span> 0.6027 56.05%<span class="token punctuation">,</span> 0.6027 56.05%<span class="token punctuation">,</span> 0.6021 59.67%<span class="token punctuation">,</span> 0.5715 63.98%<span class="token punctuation">,</span> 0.4996 71.21%<span class="token punctuation">,</span> 0.4654 75.1%<span class="token punctuation">,</span> 0.4586 78.72%<span class="token punctuation">,</span> 0.4819 83.03%<span class="token punctuation">,</span> 0.545 88.32%<span class="token punctuation">,</span> 0.5756 91.93%<span class="token punctuation">,</span> 0.586 96.38%<span class="token punctuation">,</span> 0.5677 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4991 0%<span class="token punctuation">,</span> 0.523 1.95%<span class="token punctuation">,</span> 0.5256 2.09%<span class="token punctuation">,</span> 0.5256 2.09%<span class="token punctuation">,</span> 0.5932 5.7%<span class="token punctuation">,</span> 0.6283 10.15%<span class="token punctuation">,</span> 0.6015 14.74%<span class="token punctuation">,</span> 0.5329 19.19%<span class="token punctuation">,</span> 0.4436 23.37%<span class="token punctuation">,</span> 0.4275 24.34%<span class="token punctuation">,</span> 0.4256 24.48%<span class="token punctuation">,</span> 0.4256 24.48%<span class="token punctuation">,</span> 0.4036 28.51%<span class="token punctuation">,</span> 0.4265 32.41%<span class="token punctuation">,</span> 0.4557 38.11%<span class="token punctuation">,</span> 0.5081 42.42%<span class="token punctuation">,</span> 0.619 46.87%<span class="token punctuation">,</span> 0.7462 50.49%<span class="token punctuation">,</span> 0.9254 54.52%<span class="token punctuation">,</span> 0.9915 55.91%<span class="token punctuation">,</span> 0.9983 56.05%<span class="token punctuation">,</span> 0.0069 56.05%<span class="token punctuation">,</span> 0.1584 59.67%<span class="token punctuation">,</span> 0.2816 63.98%<span class="token punctuation">,</span> 0.3996 71.21%<span class="token punctuation">,</span> 0.4163 75.1%<span class="token punctuation">,</span> 0.3807 78.72%<span class="token punctuation">,</span> 0.3096 83.03%<span class="token punctuation">,</span> 0.2578 88.32%<span class="token punctuation">,</span> 0.2865 91.93%<span class="token punctuation">,</span> 0.3568 96.38%<span class="token punctuation">,</span> 0.3885 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-2</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5667 0%<span class="token punctuation">,</span> 0.5514 3.34%<span class="token punctuation">,</span> 0.5275 7.79%<span class="token punctuation">,</span> 0.5001 11.27%<span class="token punctuation">,</span> 0.4783 14.46%<span class="token punctuation">,</span> 0.477 14.6%<span class="token punctuation">,</span> 0.477 14.6%<span class="token punctuation">,</span> 0.4733 17.52%<span class="token punctuation">,</span> 0.4992 20.72%<span class="token punctuation">,</span> 0.5581 25.03%<span class="token punctuation">,</span> 0.6456 29.76%<span class="token punctuation">,</span> 0.7902 36.16%<span class="token punctuation">,</span> 0.9636 43.12%<span class="token punctuation">,</span> 0.9971 44.92%<span class="token punctuation">,</span> 0.9996 45.06%<span class="token punctuation">,</span> 0.0062 45.06%<span class="token punctuation">,</span> 0.1062 50.9%<span class="token punctuation">,</span> 0.1416 54.38%<span class="token punctuation">,</span> 0.1537 58.69%<span class="token punctuation">,</span> 0.1571 62.31%<span class="token punctuation">,</span> 0.1843 65.79%<span class="token punctuation">,</span> 0.2541 70.65%<span class="token punctuation">,</span> 0.3115 73.57%<span class="token punctuation">,</span> 0.44 81.5%<span class="token punctuation">,</span> 0.4859 84.7%<span class="token punctuation">,</span> 0.5036 88.18%<span class="token punctuation">,</span> 0.494 92.49%<span class="token punctuation">,</span> 0.4659 97.91%<span class="token punctuation">,</span> 0.4544 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.3885 0%<span class="token punctuation">,</span> 0.354 3.34%<span class="token punctuation">,</span> 0.2468 7.79%<span class="token punctuation">,</span> 0.1326 11.27%<span class="token punctuation">,</span> 0.0064 14.46%<span class="token punctuation">,</span> 0.0013 14.6%<span class="token punctuation">,</span> 0.9927 14.6%<span class="token punctuation">,</span> 0.904 17.52%<span class="token punctuation">,</span> 0.8442 20.72%<span class="token punctuation">,</span> 0.8111 25.03%<span class="token punctuation">,</span> 0.8213 29.76%<span class="token punctuation">,</span> 0.8683 36.16%<span class="token punctuation">,</span> 0.9095 43.12%<span class="token punctuation">,</span> 0.9007 44.92%<span class="token punctuation">,</span> 0.8993 45.06%<span class="token punctuation">,</span> 0.8993 45.06%<span class="token punctuation">,</span> 0.8802 50.9%<span class="token punctuation">,</span> 0.8489 54.38%<span class="token punctuation">,</span> 0.7941 58.69%<span class="token punctuation">,</span> 0.7358 62.31%<span class="token punctuation">,</span> 0.699 65.79%<span class="token punctuation">,</span> 0.7166 70.65%<span class="token punctuation">,</span> 0.7032 73.57%<span class="token punctuation">,</span> 0.5574 81.5%<span class="token punctuation">,</span> 0.5516 84.7%<span class="token punctuation">,</span> 0.5769 88.18%<span class="token punctuation">,</span> 0.5651 92.49%<span class="token punctuation">,</span> 0.4687 97.91%<span class="token punctuation">,</span> 0.4126 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-3</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.0525 0%<span class="token punctuation">,</span> 0.0593 2.78%<span class="token punctuation">,</span> 0.0897 6.82%<span class="token punctuation">,</span> 0.1853 12.52%<span class="token punctuation">,</span> 0.2061 13.77%<span class="token punctuation">,</span> 0.2083 13.91%<span class="token punctuation">,</span> 0.2083 13.91%<span class="token punctuation">,</span> 0.2371 16.83%<span class="token punctuation">,</span> 0.2333 21.56%<span class="token punctuation">,</span> 0.2174 26.98%<span class="token punctuation">,</span> 0.1743 30.6%<span class="token punctuation">,</span> 0.0744 37.13%<span class="token punctuation">,</span> 0.0045 44.51%<span class="token punctuation">,</span> 0.003 44.65%<span class="token punctuation">,</span> 0.9965 44.65%<span class="token punctuation">,</span> 0.9775 47.84%<span class="token punctuation">,</span> 0.9753 48.96%<span class="token punctuation">,</span> 0.9753 49.1%<span class="token punctuation">,</span> 0.9753 49.1%<span class="token punctuation">,</span> 0.9819 53.27%<span class="token punctuation">,</span> 0.9967 56.05%<span class="token punctuation">,</span> 0.9977 56.19%<span class="token punctuation">,</span> 0.0042 56.19%<span class="token punctuation">,</span> 0.0386 60.78%<span class="token punctuation">,</span> 0.1144 67.45%<span class="token punctuation">,</span> 0.1632 71.07%<span class="token punctuation">,</span> 0.1782 74.83%<span class="token punctuation">,</span> 0.1612 78.72%<span class="token punctuation">,</span> 0.1259 82.2%<span class="token punctuation">,</span> 0.0391 87.9%<span class="token punctuation">,</span> 0.0043 90.26%<span class="token punctuation">,</span> 0.0022 90.4%<span class="token punctuation">,</span> 0.9957 90.4%<span class="token punctuation">,</span> 0.9592 93.74%<span class="token punctuation">,</span> 0.9086 98.75%<span class="token punctuation">,</span> 0.8941 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7101 0%<span class="token punctuation">,</span> 0.7391 2.78%<span class="token punctuation">,</span> 0.8169 6.82%<span class="token punctuation">,</span> 0.9555 12.52%<span class="token punctuation">,</span> 0.9929 13.77%<span class="token punctuation">,</span> 0.9968 13.91%<span class="token punctuation">,</span> 0.0054 13.91%<span class="token punctuation">,</span> 0.092 16.83%<span class="token punctuation">,</span> 0.2726 21.56%<span class="token punctuation">,</span> 0.5027 26.98%<span class="token punctuation">,</span> 0.6224 30.6%<span class="token punctuation">,</span> 0.764 37.13%<span class="token punctuation">,</span> 0.9324 44.51%<span class="token punctuation">,</span> 0.9355 44.65%<span class="token punctuation">,</span> 0.9355 44.65%<span class="token punctuation">,</span> 0.9853 47.84%<span class="token punctuation">,</span> 0.9948 48.96%<span class="token punctuation">,</span> 0.9957 49.1%<span class="token punctuation">,</span> 0.0043 49.1%<span class="token punctuation">,</span> 0.0564 53.27%<span class="token punctuation">,</span> 0.1153 56.05%<span class="token punctuation">,</span> 0.1188 56.19%<span class="token punctuation">,</span> 0.1188 56.19%<span class="token punctuation">,</span> 0.2095 60.78%<span class="token punctuation">,</span> 0.2992 67.45%<span class="token punctuation">,</span> 0.3022 71.07%<span class="token punctuation">,</span> 0.2834 74.83%<span class="token punctuation">,</span> 0.3143 78.72%<span class="token punctuation">,</span> 0.3698 82.2%<span class="token punctuation">,</span> 0.4719 87.9%<span class="token punctuation">,</span> 0.4761 90.26%<span class="token punctuation">,</span> 0.4756 90.4%<span class="token punctuation">,</span> 0.4756 90.4%<span class="token punctuation">,</span> 0.4853 93.74%<span class="token punctuation">,</span> 0.56 98.75%<span class="token punctuation">,</span> 0.5847 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.667<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-4</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.8927 0%<span class="token punctuation">,</span> 0.8727 3.48%<span class="token punctuation">,</span> 0.8793 7.65%<span class="token punctuation">,</span> 0.881 11.68%<span class="token punctuation">,</span> 0.8583 17.39%<span class="token punctuation">,</span> 0.8542 22.67%<span class="token punctuation">,</span> 0.8723 26.7%<span class="token punctuation">,</span> 0.9201 31.15%<span class="token punctuation">,</span> 0.9704 35.74%<span class="token punctuation">,</span> 0.9963 37.97%<span class="token punctuation">,</span> 0.9978 38.11%<span class="token punctuation">,</span> 0.0044 38.11%<span class="token punctuation">,</span> 0.0325 41.72%<span class="token punctuation">,</span> 0.0446 45.9%<span class="token punctuation">,</span> 0.0217 50.9%<span class="token punctuation">,</span> 0.0047 52.71%<span class="token punctuation">,</span> 0.0034 52.85%<span class="token punctuation">,</span> 0.9969 52.85%<span class="token punctuation">,</span> 0.9515 56.75%<span class="token punctuation">,</span> 0.8939 59.81%<span class="token punctuation">,</span> 0.8366 63.28%<span class="token punctuation">,</span> 0.7748 68.15%<span class="token punctuation">,</span> 0.7094 72.6%<span class="token punctuation">,</span> 0.6187 76.91%<span class="token punctuation">,</span> 0.5138 81.64%<span class="token punctuation">,</span> 0.3875 87.2%<span class="token punctuation">,</span> 0.1965 93.6%<span class="token punctuation">,</span> 0.0731 97.22%<span class="token punctuation">,</span> 0.0051 99.3%<span class="token punctuation">,</span> 0.0011 99.44%<span class="token punctuation">,</span> 0.9946 99.44%<span class="token punctuation">,</span> 0.9799 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5877 0%<span class="token punctuation">,</span> 0.6616 3.48%<span class="token punctuation">,</span> 0.7193 7.65%<span class="token punctuation">,</span> 0.7056 11.68%<span class="token punctuation">,</span> 0.6297 17.39%<span class="token punctuation">,</span> 0.5292 22.67%<span class="token punctuation">,</span> 0.452 26.7%<span class="token punctuation">,</span> 0.4208 31.15%<span class="token punctuation">,</span> 0.4574 35.74%<span class="token punctuation">,</span> 0.4913 37.97%<span class="token punctuation">,</span> 0.4937 38.11%<span class="token punctuation">,</span> 0.4937 38.11%<span class="token punctuation">,</span> 0.5253 41.72%<span class="token punctuation">,</span> 0.5101 45.9%<span class="token punctuation">,</span> 0.4846 50.9%<span class="token punctuation">,</span> 0.4707 52.71%<span class="token punctuation">,</span> 0.4698 52.85%<span class="token punctuation">,</span> 0.4698 52.85%<span class="token punctuation">,</span> 0.4286 56.75%<span class="token punctuation">,</span> 0.4179 59.81%<span class="token punctuation">,</span> 0.4661 63.28%<span class="token punctuation">,</span> 0.57 68.15%<span class="token punctuation">,</span> 0.698 72.6%<span class="token punctuation">,</span> 0.7804 76.91%<span class="token punctuation">,</span> 0.7977 81.64%<span class="token punctuation">,</span> 0.769 87.2%<span class="token punctuation">,</span> 0.6916 93.6%<span class="token punctuation">,</span> 0.6904 97.22%<span class="token punctuation">,</span> 0.7133 99.3%<span class="token punctuation">,</span> 0.7154 99.44%<span class="token punctuation">,</span> 0.7154 99.44%<span class="token punctuation">,</span> 0.7236 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-5</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.9767 0%<span class="token punctuation">,</span> 0.9246 2.92%<span class="token punctuation">,</span> 0.8759 8.21%<span class="token punctuation">,</span> 0.8341 13.49%<span class="token punctuation">,</span> 0.8241 18.92%<span class="token punctuation">,</span> 0.8442 23.23%<span class="token punctuation">,</span> 0.9022 28.37%<span class="token punctuation">,</span> 0.9781 33.52%<span class="token punctuation">,</span> 0.9972 34.63%<span class="token punctuation">,</span> 0.9996 34.77%<span class="token punctuation">,</span> 0.0062 34.77%<span class="token punctuation">,</span> 0.0626 38.94%<span class="token punctuation">,</span> 0.0944 42.56%<span class="token punctuation">,</span> 0.1044 46.45%<span class="token punctuation">,</span> 0.0921 51.46%<span class="token punctuation">,</span> 0.1252 57.16%<span class="token punctuation">,</span> 0.17 62.87%<span class="token punctuation">,</span> 0.1894 66.34%<span class="token punctuation">,</span> 0.1756 70.51%<span class="token punctuation">,</span> 0.1228 74.97%<span class="token punctuation">,</span> 0.0228 81.64%<span class="token punctuation">,</span> 0.0052 82.75%<span class="token punctuation">,</span> 0.0031 82.89%<span class="token punctuation">,</span> 0.9965 82.89%<span class="token punctuation">,</span> 0.9627 86.93%<span class="token punctuation">,</span> 0.9666 90.26%<span class="token punctuation">,</span> 0.9958 93.6%<span class="token punctuation">,</span> 0.9974 93.74%<span class="token punctuation">,</span> 0.0039 93.74%<span class="token punctuation">,</span> 0.0156 94.85%<span class="token punctuation">,</span> 0.0169 94.99%<span class="token punctuation">,</span> 0.0169 94.99%<span class="token punctuation">,</span> 0.0543 97.91%<span class="token punctuation">,</span> 0.0835 99.72%<span class="token punctuation">,</span> 0.0857 99.86%<span class="token punctuation">,</span> 0.0857 99.86%<span class="token punctuation">,</span> 0.0878 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7258 0%<span class="token punctuation">,</span> 0.7484 2.92%<span class="token punctuation">,</span> 0.734 8.21%<span class="token punctuation">,</span> 0.6713 13.49%<span class="token punctuation">,</span> 0.5914 18.92%<span class="token punctuation">,</span> 0.5174 23.23%<span class="token punctuation">,</span> 0.4799 28.37%<span class="token punctuation">,</span> 0.4928 33.52%<span class="token punctuation">,</span> 0.5032 34.63%<span class="token punctuation">,</span> 0.5048 34.77%<span class="token punctuation">,</span> 0.5048 34.77%<span class="token punctuation">,</span> 0.5264 38.94%<span class="token punctuation">,</span> 0.5027 42.56%<span class="token punctuation">,</span> 0.4368 46.45%<span class="token punctuation">,</span> 0.3336 51.46%<span class="token punctuation">,</span> 0.2363 57.16%<span class="token punctuation">,</span> 0.2015 62.87%<span class="token punctuation">,</span> 0.1771 66.34%<span class="token punctuation">,</span> 0.1236 70.51%<span class="token punctuation">,</span> 0.1045 74.97%<span class="token punctuation">,</span> 0.1079 81.64%<span class="token punctuation">,</span> 0.1146 82.75%<span class="token punctuation">,</span> 0.1155 82.89%<span class="token punctuation">,</span> 0.1155 82.89%<span class="token punctuation">,</span> 0.1187 86.93%<span class="token punctuation">,</span> 0.0909 90.26%<span class="token punctuation">,</span> 0.0303 93.6%<span class="token punctuation">,</span> 0.0271 93.74%<span class="token punctuation">,</span> 0.0271 93.74%<span class="token punctuation">,</span> 0.0045 94.85%<span class="token punctuation">,</span> 0.0021 94.99%<span class="token punctuation">,</span> 0.9935 94.99%<span class="token punctuation">,</span> 0.9861 97.91%<span class="token punctuation">,</span> 0.9943 99.72%<span class="token punctuation">,</span> 0.9951 99.86%<span class="token punctuation">,</span> 0.0038 99.86%<span class="token punctuation">,</span> 0.0045 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-6</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.2039 0%<span class="token punctuation">,</span> 0.2116 4.03%<span class="token punctuation">,</span> 0.1899 7.37%<span class="token punctuation">,</span> 0.1166 13.07%<span class="token punctuation">,</span> 0.0536 18.92%<span class="token punctuation">,</span> 0.005 21.84%<span class="token punctuation">,</span> 0.0022 21.97%<span class="token punctuation">,</span> 0.9956 21.97%<span class="token punctuation">,</span> 0.9224 25.73%<span class="token punctuation">,</span> 0.8737 29.35%<span class="token punctuation">,</span> 0.8509 33.1%<span class="token punctuation">,</span> 0.8615 38.94%<span class="token punctuation">,</span> 0.8932 43.25%<span class="token punctuation">,</span> 0.9264 46.87%<span class="token punctuation">,</span> 0.9303 50.63%<span class="token punctuation">,</span> 0.8827 55.49%<span class="token punctuation">,</span> 0.8148 59.11%<span class="token punctuation">,</span> 0.6782 63.98%<span class="token punctuation">,</span> 0.5721 67.59%<span class="token punctuation">,</span> 0.4709 72.32%<span class="token punctuation">,</span> 0.449 73.44%<span class="token punctuation">,</span> 0.4462 73.57%<span class="token punctuation">,</span> 0.4462 73.57%<span class="token punctuation">,</span> 0.4158 76.22%<span class="token punctuation">,</span> 0.4136 79.42%<span class="token punctuation">,</span> 0.46 84.56%<span class="token punctuation">,</span> 0.5393 89.15%<span class="token punctuation">,</span> 0.6701 95.83%<span class="token punctuation">,</span> 0.737 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.124 0%<span class="token punctuation">,</span> 0.16 4.03%<span class="token punctuation">,</span> 0.2154 7.37%<span class="token punctuation">,</span> 0.3414 13.07%<span class="token punctuation">,</span> 0.5443 18.92%<span class="token punctuation">,</span> 0.6413 21.84%<span class="token punctuation">,</span> 0.645 21.97%<span class="token punctuation">,</span> 0.645 21.97%<span class="token punctuation">,</span> 0.7116 25.73%<span class="token punctuation">,</span> 0.7043 29.35%<span class="token punctuation">,</span> 0.6505 33.1%<span class="token punctuation">,</span> 0.5392 38.94%<span class="token punctuation">,</span> 0.487 43.25%<span class="token punctuation">,</span> 0.5023 46.87%<span class="token punctuation">,</span> 0.5527 50.63%<span class="token punctuation">,</span> 0.627 55.49%<span class="token punctuation">,</span> 0.6591 59.11%<span class="token punctuation">,</span> 0.7213 63.98%<span class="token punctuation">,</span> 0.8065 67.59%<span class="token punctuation">,</span> 0.9541 72.32%<span class="token punctuation">,</span> 0.9942 73.44%<span class="token punctuation">,</span> 0.9992 73.57%<span class="token punctuation">,</span> 0.0078 73.57%<span class="token punctuation">,</span> 0.0824 76.22%<span class="token punctuation">,</span> 0.1196 79.42%<span class="token punctuation">,</span> 0.1231 84.56%<span class="token punctuation">,</span> 0.1277 89.15%<span class="token punctuation">,</span> 0.1896 95.83%<span class="token punctuation">,</span> 0.2652 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.667<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-7</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7387 0%<span class="token punctuation">,</span> 0.7573 2.92%<span class="token punctuation">,</span> 0.735 6.12%<span class="token punctuation">,</span> 0.6667 10.71%<span class="token punctuation">,</span> 0.6152 14.46%<span class="token punctuation">,</span> 0.6097 18.5%<span class="token punctuation">,</span> 0.6177 21.7%<span class="token punctuation">,</span> 0.6463 26.01%<span class="token punctuation">,</span> 0.7313 33.1%<span class="token punctuation">,</span> 0.8374 40.19%<span class="token punctuation">,</span> 0.8835 43.67%<span class="token punctuation">,</span> 0.8855 46.87%<span class="token punctuation">,</span> 0.8426 51.04%<span class="token punctuation">,</span> 0.7793 54.8%<span class="token punctuation">,</span> 0.748 58.69%<span class="token punctuation">,</span> 0.7483 64.26%<span class="token punctuation">,</span> 0.7241 69.26%<span class="token punctuation">,</span> 0.6705 72.46%<span class="token punctuation">,</span> 0.5708 76.63%<span class="token punctuation">,</span> 0.309 86.65%<span class="token punctuation">,</span> 0.2062 91.1%<span class="token punctuation">,</span> 0.1652 94.16%<span class="token punctuation">,</span> 0.1606 95.97%<span class="token punctuation">,</span> 0.1606 96.11%<span class="token punctuation">,</span> 0.1606 96.11%<span class="token punctuation">,</span> 0.1628 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.2679 0%<span class="token punctuation">,</span> 0.3289 2.92%<span class="token punctuation">,</span> 0.3934 6.12%<span class="token punctuation">,</span> 0.4819 10.71%<span class="token punctuation">,</span> 0.5946 14.46%<span class="token punctuation">,</span> 0.6969 18.5%<span class="token punctuation">,</span> 0.7217 21.7%<span class="token punctuation">,</span> 0.6866 26.01%<span class="token punctuation">,</span> 0.5779 33.1%<span class="token punctuation">,</span> 0.4034 40.19%<span class="token punctuation">,</span> 0.3703 43.67%<span class="token punctuation">,</span> 0.372 46.87%<span class="token punctuation">,</span> 0.3759 51.04%<span class="token punctuation">,</span> 0.3957 54.8%<span class="token punctuation">,</span> 0.4735 58.69%<span class="token punctuation">,</span> 0.6217 64.26%<span class="token punctuation">,</span> 0.7581 69.26%<span class="token punctuation">,</span> 0.8356 72.46%<span class="token punctuation">,</span> 0.8886 76.63%<span class="token punctuation">,</span> 0.8969 86.65%<span class="token punctuation">,</span> 0.9173 91.1%<span class="token punctuation">,</span> 0.9594 94.16%<span class="token punctuation">,</span> 0.9938 95.97%<span class="token punctuation">,</span> 0.9966 96.11%<span class="token punctuation">,</span> 0.0052 96.11%<span class="token punctuation">,</span> 0.0594 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-8</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.1631 0%<span class="token punctuation">,</span> 0.1891 4.17%<span class="token punctuation">,</span> 0.2179 7.93%<span class="token punctuation">,</span> 0.2064 11.96%<span class="token punctuation">,</span> 0.1691 16.55%<span class="token punctuation">,</span> 0.1439 21.14%<span class="token punctuation">,</span> 0.1155 25.17%<span class="token punctuation">,</span> 0.0416 30.6%<span class="token punctuation">,</span> 0.0045 33.24%<span class="token punctuation">,</span> 0.0027 33.38%<span class="token punctuation">,</span> 0.9961 33.38%<span class="token punctuation">,</span> 0.9687 36.58%<span class="token punctuation">,</span> 0.9715 41.03%<span class="token punctuation">,</span> 0.9971 44.23%<span class="token punctuation">,</span> 0.9986 44.37%<span class="token punctuation">,</span> 0.0052 44.37%<span class="token punctuation">,</span> 0.0487 50.35%<span class="token punctuation">,</span> 0.0361 53.96%<span class="token punctuation">,</span> 0.0041 58.14%<span class="token punctuation">,</span> 0.0034 58.28%<span class="token punctuation">,</span> 0.9968 58.28%<span class="token punctuation">,</span> 0.9579 62.31%<span class="token punctuation">,</span> 0.8978 67.04%<span class="token punctuation">,</span> 0.812 73.3%<span class="token punctuation">,</span> 0.7325 78.16%<span class="token punctuation">,</span> 0.6638 81.5%<span class="token punctuation">,</span> 0.5726 86.37%<span class="token punctuation">,</span> 0.4788 91.93%<span class="token punctuation">,</span> 0.4219 96.11%<span class="token punctuation">,</span> 0.3739 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.0606 0%<span class="token punctuation">,</span> 0.0837 4.17%<span class="token punctuation">,</span> 0.1412 7.93%<span class="token punctuation">,</span> 0.2038 11.96%<span class="token punctuation">,</span> 0.2998 16.55%<span class="token punctuation">,</span> 0.4406 21.14%<span class="token punctuation">,</span> 0.5743 25.17%<span class="token punctuation">,</span> 0.6941 30.6%<span class="token punctuation">,</span> 0.7455 33.24%<span class="token punctuation">,</span> 0.7482 33.38%<span class="token punctuation">,</span> 0.7482 33.38%<span class="token punctuation">,</span> 0.7782 36.58%<span class="token punctuation">,</span> 0.7626 41.03%<span class="token punctuation">,</span> 0.7221 44.23%<span class="token punctuation">,</span> 0.7201 44.37%<span class="token punctuation">,</span> 0.7201 44.37%<span class="token punctuation">,</span> 0.6337 50.35%<span class="token punctuation">,</span> 0.5698 53.96%<span class="token punctuation">,</span> 0.4997 58.14%<span class="token punctuation">,</span> 0.4979 58.28%<span class="token punctuation">,</span> 0.4979 58.28%<span class="token punctuation">,</span> 0.4684 62.31%<span class="token punctuation">,</span> 0.4851 67.04%<span class="token punctuation">,</span> 0.5941 73.3%<span class="token punctuation">,</span> 0.6739 78.16%<span class="token punctuation">,</span> 0.7036 81.5%<span class="token punctuation">,</span> 0.6711 86.37%<span class="token punctuation">,</span> 0.5736 91.93%<span class="token punctuation">,</span> 0.456 96.11%<span class="token punctuation">,</span> 0.3235 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-9</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.6271 0%<span class="token punctuation">,</span> 0.6199 4.03%<span class="token punctuation">,</span> 0.6022 7.93%<span class="token punctuation">,</span> 0.558 13.77%<span class="token punctuation">,</span> 0.5301 16.41%<span class="token punctuation">,</span> 0.5282 16.55%<span class="token punctuation">,</span> 0.5282 16.55%<span class="token punctuation">,</span> 0.5095 19.75%<span class="token punctuation">,</span> 0.5262 23.37%<span class="token punctuation">,</span> 0.5668 26.43%<span class="token punctuation">,</span> 0.6946 33.1%<span class="token punctuation">,</span> 0.7925 36.72%<span class="token punctuation">,</span> 0.9063 40.75%<span class="token punctuation">,</span> 0.9969 45.06%<span class="token punctuation">,</span> 0.9997 45.2%<span class="token punctuation">,</span> 0.0062 45.2%<span class="token punctuation">,</span> 0.0839 49.93%<span class="token punctuation">,</span> 0.1125 53.96%<span class="token punctuation">,</span> 0.1238 60.22%<span class="token punctuation">,</span> 0.1544 64.81%<span class="token punctuation">,</span> 0.1902 68.71%<span class="token punctuation">,</span> 0.2354 72.32%<span class="token punctuation">,</span> 0.2638 76.08%<span class="token punctuation">,</span> 0.249 79.55%<span class="token punctuation">,</span> 0.1861 86.51%<span class="token punctuation">,</span> 0.1355 92.49%<span class="token punctuation">,</span> 0.086 99.3%<span class="token punctuation">,</span> 0.0843 99.44%<span class="token punctuation">,</span> 0.0843 99.44%<span class="token punctuation">,</span> 0.0789 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4129 0%<span class="token punctuation">,</span> 0.3703 4.03%<span class="token punctuation">,</span> 0.2823 7.93%<span class="token punctuation">,</span> 0.1055 13.77%<span class="token punctuation">,</span> 0.0076 16.41%<span class="token punctuation">,</span> 0.0023 16.55%<span class="token punctuation">,</span> 0.9936 16.55%<span class="token punctuation">,</span> 0.8871 19.75%<span class="token punctuation">,</span> 0.8084 23.37%<span class="token punctuation">,</span> 0.7834 26.43%<span class="token punctuation">,</span> 0.8071 33.1%<span class="token punctuation">,</span> 0.8172 36.72%<span class="token punctuation">,</span> 0.7852 40.75%<span class="token punctuation">,</span> 0.721 45.06%<span class="token punctuation">,</span> 0.7195 45.2%<span class="token punctuation">,</span> 0.7195 45.2%<span class="token punctuation">,</span> 0.6669 49.93%<span class="token punctuation">,</span> 0.6231 53.96%<span class="token punctuation">,</span> 0.5377 60.22%<span class="token punctuation">,</span> 0.5224 64.81%<span class="token punctuation">,</span> 0.5538 68.71%<span class="token punctuation">,</span> 0.5711 72.32%<span class="token punctuation">,</span> 0.5257 76.08%<span class="token punctuation">,</span> 0.4784 79.55%<span class="token punctuation">,</span> 0.3752 86.51%<span class="token punctuation">,</span> 0.2173 92.49%<span class="token punctuation">,</span> 0.0067 99.3%<span class="token punctuation">,</span> 0.002 99.44%<span class="token punctuation">,</span> 0.9934 99.44%<span class="token punctuation">,</span> 0.9759 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.667<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-10</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.0779 0%<span class="token punctuation">,</span> 0.0765 3.06%<span class="token punctuation">,</span> 0.109 6.82%<span class="token punctuation">,</span> 0.1859 11.27%<span class="token punctuation">,</span> 0.299 16.13%<span class="token punctuation">,</span> 0.4576 21.84%<span class="token punctuation">,</span> 0.5676 26.15%<span class="token punctuation">,</span> 0.6826 31.15%<span class="token punctuation">,</span> 0.6859 31.29%<span class="token punctuation">,</span> 0.6859 31.29%<span class="token punctuation">,</span> 0.7278 34.21%<span class="token punctuation">,</span> 0.7337 37.41%<span class="token punctuation">,</span> 0.6925 42.42%<span class="token punctuation">,</span> 0.6414 47.15%<span class="token punctuation">,</span> 0.5882 53.41%<span class="token punctuation">,</span> 0.5629 57.86%<span class="token punctuation">,</span> 0.5619 58%<span class="token punctuation">,</span> 0.5619 58%<span class="token punctuation">,</span> 0.5061 63.28%<span class="token punctuation">,</span> 0.3916 72.46%<span class="token punctuation">,</span> 0.3458 77.05%<span class="token punctuation">,</span> 0.3077 80.67%<span class="token punctuation">,</span> 0.2762 84.98%<span class="token punctuation">,</span> 0.2814 89.01%<span class="token punctuation">,</span> 0.3106 92.63%<span class="token punctuation">,</span> 0.3121 95.83%<span class="token punctuation">,</span> 0.2791 98.89%<span class="token punctuation">,</span> 0.2608 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.9717 0%<span class="token punctuation">,</span> 0.9046 3.06%<span class="token punctuation">,</span> 0.8462 6.82%<span class="token punctuation">,</span> 0.803 11.27%<span class="token punctuation">,</span> 0.781 16.13%<span class="token punctuation">,</span> 0.7978 21.84%<span class="token punctuation">,</span> 0.8646 26.15%<span class="token punctuation">,</span> 0.9941 31.15%<span class="token punctuation">,</span> 0.9985 31.29%<span class="token punctuation">,</span> 0.0071 31.29%<span class="token punctuation">,</span> 0.0848 34.21%<span class="token punctuation">,</span> 0.1599 37.41%<span class="token punctuation">,</span> 0.2976 42.42%<span class="token punctuation">,</span> 0.4912 47.15%<span class="token punctuation">,</span> 0.7867 53.41%<span class="token punctuation">,</span> 0.9893 57.86%<span class="token punctuation">,</span> 0.9955 58%<span class="token punctuation">,</span> 0.0041 58%<span class="token punctuation">,</span> 0.1916 63.28%<span class="token punctuation">,</span> 0.4203 72.46%<span class="token punctuation">,</span> 0.4937 77.05%<span class="token punctuation">,</span> 0.5014 80.67%<span class="token punctuation">,</span> 0.4638 84.98%<span class="token punctuation">,</span> 0.4198 89.01%<span class="token punctuation">,</span> 0.418 92.63%<span class="token punctuation">,</span> 0.4423 95.83%<span class="token punctuation">,</span> 0.4507 98.89%<span class="token punctuation">,</span> 0.4463 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-11</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.2585 0%<span class="token punctuation">,</span> 0.166 3.89%<span class="token punctuation">,</span> 0.0857 7.09%<span class="token punctuation">,</span> 0.0336 10.01%<span class="token punctuation">,</span> 0.0042 12.8%<span class="token punctuation">,</span> 0.0025 12.93%<span class="token punctuation">,</span> 0.9959 12.93%<span class="token punctuation">,</span> 0.9432 17.66%<span class="token punctuation">,</span> 0.8861 22.11%<span class="token punctuation">,</span> 0.8295 26.43%<span class="token punctuation">,</span> 0.8039 29.76%<span class="token punctuation">,</span> 0.8118 34.08%<span class="token punctuation">,</span> 0.8584 40.19%<span class="token punctuation">,</span> 0.9103 43.39%<span class="token punctuation">,</span> 0.9641 47.29%<span class="token punctuation">,</span> 0.9784 51.46%<span class="token punctuation">,</span> 0.9569 54.52%<span class="token punctuation">,</span> 0.8855 58.69%<span class="token punctuation">,</span> 0.8102 62.31%<span class="token punctuation">,</span> 0.764 65.51%<span class="token punctuation">,</span> 0.687 72.04%<span class="token punctuation">,</span> 0.6085 75.8%<span class="token punctuation">,</span> 0.4921 81.22%<span class="token punctuation">,</span> 0.3838 86.51%<span class="token punctuation">,</span> 0.2429 91.66%<span class="token punctuation">,</span> 0.188 94.85%<span class="token punctuation">,</span> 0.1699 98.33%<span class="token punctuation">,</span> 0.1714 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4459 0%<span class="token punctuation">,</span> 0.4441 3.89%<span class="token punctuation">,</span> 0.4796 7.09%<span class="token punctuation">,</span> 0.5529 10.01%<span class="token punctuation">,</span> 0.6489 12.8%<span class="token punctuation">,</span> 0.6539 12.93%<span class="token punctuation">,</span> 0.6539 12.93%<span class="token punctuation">,</span> 0.7897 17.66%<span class="token punctuation">,</span> 0.8486 22.11%<span class="token punctuation">,</span> 0.8393 26.43%<span class="token punctuation">,</span> 0.7897 29.76%<span class="token punctuation">,</span> 0.6961 34.08%<span class="token punctuation">,</span> 0.5542 40.19%<span class="token punctuation">,</span> 0.5105 43.39%<span class="token punctuation">,</span> 0.5047 47.29%<span class="token punctuation">,</span> 0.5246 51.46%<span class="token punctuation">,</span> 0.5113 54.52%<span class="token punctuation">,</span> 0.473 58.69%<span class="token punctuation">,</span> 0.4944 62.31%<span class="token punctuation">,</span> 0.5678 65.51%<span class="token punctuation">,</span> 0.7636 72.04%<span class="token punctuation">,</span> 0.8309 75.8%<span class="token punctuation">,</span> 0.8629 81.22%<span class="token punctuation">,</span> 0.8529 86.51%<span class="token punctuation">,</span> 0.8592 91.66%<span class="token punctuation">,</span> 0.9054 94.85%<span class="token punctuation">,</span> 0.9699 98.33%<span class="token punctuation">,</span> 0.9882 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-12</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.9259 0%<span class="token punctuation">,</span> 0.9389 4.73%<span class="token punctuation">,</span> 0.9971 11.96%<span class="token punctuation">,</span> 0.9983 12.1%<span class="token punctuation">,</span> 0.0049 12.1%<span class="token punctuation">,</span> 0.0236 14.74%<span class="token punctuation">,</span> 0.0257 18.22%<span class="token punctuation">,</span> 0.0047 20.58%<span class="token punctuation">,</span> 0.003 20.72%<span class="token punctuation">,</span> 0.9964 20.72%<span class="token punctuation">,</span> 0.9378 25.73%<span class="token punctuation">,</span> 0.907 29.35%<span class="token punctuation">,</span> 0.9195 33.24%<span class="token punctuation">,</span> 0.9754 37.83%<span class="token punctuation">,</span> 0.9954 39.22%<span class="token punctuation">,</span> 0.9975 39.36%<span class="token punctuation">,</span> 0.0041 39.36%<span class="token punctuation">,</span> 0.0408 42.84%<span class="token punctuation">,</span> 0.0586 46.87%<span class="token punctuation">,</span> 0.0327 50.49%<span class="token punctuation">,</span> 0.0044 52.85%<span class="token punctuation">,</span> 0.0027 52.99%<span class="token punctuation">,</span> 0.9961 52.99%<span class="token punctuation">,</span> 0.907 58.97%<span class="token punctuation">,</span> 0.8669 61.89%<span class="token punctuation">,</span> 0.8419 65.37%<span class="token punctuation">,</span> 0.8118 70.38%<span class="token punctuation">,</span> 0.7607 73.44%<span class="token punctuation">,</span> 0.6946 76.77%<span class="token punctuation">,</span> 0.5515 83.17%<span class="token punctuation">,</span> 0.4293 88.32%<span class="token punctuation">,</span> 0.2158 95.69%<span class="token punctuation">,</span> 0.0975 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7091 0%<span class="token punctuation">,</span> 0.6901 4.73%<span class="token punctuation">,</span> 0.5984 11.96%<span class="token punctuation">,</span> 0.5956 12.1%<span class="token punctuation">,</span> 0.5956 12.1%<span class="token punctuation">,</span> 0.5753 14.74%<span class="token punctuation">,</span> 0.6053 18.22%<span class="token punctuation">,</span> 0.631 20.58%<span class="token punctuation">,</span> 0.6327 20.72%<span class="token punctuation">,</span> 0.6327 20.72%<span class="token punctuation">,</span> 0.6518 25.73%<span class="token punctuation">,</span> 0.6313 29.35%<span class="token punctuation">,</span> 0.5858 33.24%<span class="token punctuation">,</span> 0.5372 37.83%<span class="token punctuation">,</span> 0.5337 39.22%<span class="token punctuation">,</span> 0.5339 39.36%<span class="token punctuation">,</span> 0.5339 39.36%<span class="token punctuation">,</span> 0.5164 42.84%<span class="token punctuation">,</span> 0.4696 46.87%<span class="token punctuation">,</span> 0.4656 50.49%<span class="token punctuation">,</span> 0.4596 52.85%<span class="token punctuation">,</span> 0.4592 52.99%<span class="token punctuation">,</span> 0.4592 52.99%<span class="token punctuation">,</span> 0.3989 58.97%<span class="token punctuation">,</span> 0.4167 61.89%<span class="token punctuation">,</span> 0.4872 65.37%<span class="token punctuation">,</span> 0.6502 70.38%<span class="token punctuation">,</span> 0.718 73.44%<span class="token punctuation">,</span> 0.7373 76.77%<span class="token punctuation">,</span> 0.6846 83.17%<span class="token punctuation">,</span> 0.6028 88.32%<span class="token punctuation">,</span> 0.4891 95.69%<span class="token punctuation">,</span> 0.4727 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.667<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-13</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.0937 0%<span class="token punctuation">,</span> 0.0063 3.34%<span class="token punctuation">,</span> 0.0027 3.48%<span class="token punctuation">,</span> 0.9962 3.48%<span class="token punctuation">,</span> 0.9359 6.68%<span class="token punctuation">,</span> 0.9122 10.15%<span class="token punctuation">,</span> 0.8963 14.88%<span class="token punctuation">,</span> 0.8877 19.89%<span class="token punctuation">,</span> 0.9015 23.5%<span class="token punctuation">,</span> 0.9446 27.12%<span class="token punctuation">,</span> 0.9969 30.88%<span class="token punctuation">,</span> 0.9985 31.02%<span class="token punctuation">,</span> 0.005 31.02%<span class="token punctuation">,</span> 0.0501 36.16%<span class="token punctuation">,</span> 0.0776 40.19%<span class="token punctuation">,</span> 0.0822 44.65%<span class="token punctuation">,</span> 0.0509 49.93%<span class="token punctuation">,</span> 0.026 55.35%<span class="token punctuation">,</span> 0.0358 60.08%<span class="token punctuation">,</span> 0.067 66.9%<span class="token punctuation">,</span> 0.0935 70.79%<span class="token punctuation">,</span> 0.0926 74.55%<span class="token punctuation">,</span> 0.0565 79.14%<span class="token punctuation">,</span> 0.006 82.2%<span class="token punctuation">,</span> 0.0036 82.34%<span class="token punctuation">,</span> 0.9971 82.34%<span class="token punctuation">,</span> 0.9487 85.95%<span class="token punctuation">,</span> 0.9413 88.87%<span class="token punctuation">,</span> 0.9761 94.02%<span class="token punctuation">,</span> 0.9952 95.55%<span class="token punctuation">,</span> 0.9971 95.69%<span class="token punctuation">,</span> 0.9971 95.69%<span class="token punctuation">,</span> 0.9991 95.83%<span class="token punctuation">,</span> 0.0056 95.83%<span class="token punctuation">,</span> 0.0486 99.03%<span class="token punctuation">,</span> 0.0634 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4732 0%<span class="token punctuation">,</span> 0.5127 3.34%<span class="token punctuation">,</span> 0.5157 3.48%<span class="token punctuation">,</span> 0.5157 3.48%<span class="token punctuation">,</span> 0.5832 6.68%<span class="token punctuation">,</span> 0.6082 10.15%<span class="token punctuation">,</span> 0.5852 14.88%<span class="token punctuation">,</span> 0.5139 19.89%<span class="token punctuation">,</span> 0.4605 23.5%<span class="token punctuation">,</span> 0.4448 27.12%<span class="token punctuation">,</span> 0.473 30.88%<span class="token punctuation">,</span> 0.4747 31.02%<span class="token punctuation">,</span> 0.4747 31.02%<span class="token punctuation">,</span> 0.5096 36.16%<span class="token punctuation">,</span> 0.472 40.19%<span class="token punctuation">,</span> 0.4158 44.65%<span class="token punctuation">,</span> 0.3838 49.93%<span class="token punctuation">,</span> 0.3038 55.35%<span class="token punctuation">,</span> 0.2641 60.08%<span class="token punctuation">,</span> 0.2491 66.9%<span class="token punctuation">,</span> 0.2293 70.79%<span class="token punctuation">,</span> 0.1872 74.55%<span class="token punctuation">,</span> 0.1561 79.14%<span class="token punctuation">,</span> 0.1545 82.2%<span class="token punctuation">,</span> 0.155 82.34%<span class="token punctuation">,</span> 0.155 82.34%<span class="token punctuation">,</span> 0.1603 85.95%<span class="token punctuation">,</span> 0.1273 88.87%<span class="token punctuation">,</span> 0.0353 94.02%<span class="token punctuation">,</span> 0.0042 95.55%<span class="token punctuation">,</span> 0.0014 95.69%<span class="token punctuation">,</span> 0.9928 95.69%<span class="token punctuation">,</span> 0.9901 95.83%<span class="token punctuation">,</span> 0.9901 95.83%<span class="token punctuation">,</span> 0.9599 99.03%<span class="token punctuation">,</span> 0.9568 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-14</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.0655 0%<span class="token punctuation">,</span> 0.1601 4.59%<span class="token punctuation">,</span> 0.2991 10.15%<span class="token punctuation">,</span> 0.4399 15.44%<span class="token punctuation">,</span> 0.5877 21.14%<span class="token punctuation">,</span> 0.7302 26.56%<span class="token punctuation">,</span> 0.8514 31.71%<span class="token punctuation">,</span> 0.964 37.41%<span class="token punctuation">,</span> 0.9954 39.5%<span class="token punctuation">,</span> 0.9976 39.64%<span class="token punctuation">,</span> 0.0042 39.64%<span class="token punctuation">,</span> 0.0705 44.23%<span class="token punctuation">,</span> 0.0844 47.57%<span class="token punctuation">,</span> 0.0421 54.52%<span class="token punctuation">,</span> 0.0335 58.28%<span class="token punctuation">,</span> 0.0468 63.42%<span class="token punctuation">,</span> 0.0763 67.32%<span class="token punctuation">,</span> 0.1318 71.91%<span class="token punctuation">,</span> 0.1336 76.08%<span class="token punctuation">,</span> 0.0981 80.67%<span class="token punctuation">,</span> 0.0629 85.26%<span class="token punctuation">,</span> 0.0239 89.85%<span class="token punctuation">,</span> 0.005 91.38%<span class="token punctuation">,</span> 0.003 91.52%<span class="token punctuation">,</span> 0.9964 91.52%<span class="token punctuation">,</span> 0.962 95.41%<span class="token punctuation">,</span> 0.9398 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.9566 0%<span class="token punctuation">,</span> 0.9551 4.59%<span class="token punctuation">,</span> 0.9226 10.15%<span class="token punctuation">,</span> 0.8918 15.44%<span class="token punctuation">,</span> 0.9073 21.14%<span class="token punctuation">,</span> 0.8789 26.56%<span class="token punctuation">,</span> 0.7977 31.71%<span class="token punctuation">,</span> 0.6606 37.41%<span class="token punctuation">,</span> 0.6182 39.5%<span class="token punctuation">,</span> 0.6158 39.64%<span class="token punctuation">,</span> 0.6158 39.64%<span class="token punctuation">,</span> 0.5395 44.23%<span class="token punctuation">,</span> 0.4797 47.57%<span class="token punctuation">,</span> 0.3698 54.52%<span class="token punctuation">,</span> 0.3082 58.28%<span class="token punctuation">,</span> 0.2822 63.42%<span class="token punctuation">,</span> 0.2986 67.32%<span class="token punctuation">,</span> 0.3065 71.91%<span class="token punctuation">,</span> 0.2686 76.08%<span class="token punctuation">,</span> 0.2379 80.67%<span class="token punctuation">,</span> 0.2717 85.26%<span class="token punctuation">,</span> 0.3371 89.85%<span class="token punctuation">,</span> 0.3535 91.38%<span class="token punctuation">,</span> 0.3545 91.52%<span class="token punctuation">,</span> 0.3545 91.52%<span class="token punctuation">,</span> 0.4071 95.41%<span class="token punctuation">,</span> 0.518 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-15</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.1467 0%<span class="token punctuation">,</span> 0.1305 3.06%<span class="token punctuation">,</span> 0.0833 7.09%<span class="token punctuation">,</span> 0.0411 11.13%<span class="token punctuation">,</span> 0.0096 16.55%<span class="token punctuation">,</span> 0.0045 17.11%<span class="token punctuation">,</span> 0.0031 17.25%<span class="token punctuation">,</span> 0.9965 17.25%<span class="token punctuation">,</span> 0.9553 21.28%<span class="token punctuation">,</span> 0.9098 26.56%<span class="token punctuation">,</span> 0.8794 30.46%<span class="token punctuation">,</span> 0.8753 34.49%<span class="token punctuation">,</span> 0.8955 41.31%<span class="token punctuation">,</span> 0.9325 45.9%<span class="token punctuation">,</span> 0.9752 49.79%<span class="token punctuation">,</span> 0.9801 53.27%<span class="token punctuation">,</span> 0.9345 58.14%<span class="token punctuation">,</span> 0.8737 61.75%<span class="token punctuation">,</span> 0.793 65.51%<span class="token punctuation">,</span> 0.7023 70.1%<span class="token punctuation">,</span> 0.5791 77.89%<span class="token punctuation">,</span> 0.5058 81.36%<span class="token punctuation">,</span> 0.3807 86.51%<span class="token punctuation">,</span> 0.1884 92.77%<span class="token punctuation">,</span> 0.0886 96.11%<span class="token punctuation">,</span> 0.0221 99.03%<span class="token punctuation">,</span> 0.0072 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.3304 0%<span class="token punctuation">,</span> 0.348 3.06%<span class="token punctuation">,</span> 0.4249 7.09%<span class="token punctuation">,</span> 0.5587 11.13%<span class="token punctuation">,</span> 0.7697 16.55%<span class="token punctuation">,</span> 0.7858 17.11%<span class="token punctuation">,</span> 0.7897 17.25%<span class="token punctuation">,</span> 0.7897 17.25%<span class="token punctuation">,</span> 0.8886 21.28%<span class="token punctuation">,</span> 0.9251 26.56%<span class="token punctuation">,</span> 0.9092 30.46%<span class="token punctuation">,</span> 0.8348 34.49%<span class="token punctuation">,</span> 0.6999 41.31%<span class="token punctuation">,</span> 0.6166 45.9%<span class="token punctuation">,</span> 0.6071 49.79%<span class="token punctuation">,</span> 0.6328 53.27%<span class="token punctuation">,</span> 0.6375 58.14%<span class="token punctuation">,</span> 0.5955 61.75%<span class="token punctuation">,</span> 0.5695 65.51%<span class="token punctuation">,</span> 0.6172 70.1%<span class="token punctuation">,</span> 0.7789 77.89%<span class="token punctuation">,</span> 0.8033 81.36%<span class="token punctuation">,</span> 0.7924 86.51%<span class="token punctuation">,</span> 0.746 92.77%<span class="token punctuation">,</span> 0.7712 96.11%<span class="token punctuation">,</span> 0.8401 99.03%<span class="token punctuation">,</span> 0.87 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.667<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-16</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.0055 0%<span class="token punctuation">,</span> 0.0039 0.14%<span class="token punctuation">,</span> 0.0025 0.28%<span class="token punctuation">,</span> 0.996 0.28%<span class="token punctuation">,</span> 0.9794 2.92%<span class="token punctuation">,</span> 0.9616 8.21%<span class="token punctuation">,</span> 0.9526 9.74%<span class="token punctuation">,</span> 0.9517 9.87%<span class="token punctuation">,</span> 0.9517 9.87%<span class="token punctuation">,</span> 0.8799 17.52%<span class="token punctuation">,</span> 0.822 21.84%<span class="token punctuation">,</span> 0.8151 22.39%<span class="token punctuation">,</span> 0.8136 22.53%<span class="token punctuation">,</span> 0.8136 22.53%<span class="token punctuation">,</span> 0.7711 27.26%<span class="token punctuation">,</span> 0.7708 32.55%<span class="token punctuation">,</span> 0.8278 39.5%<span class="token punctuation">,</span> 0.8703 43.25%<span class="token punctuation">,</span> 0.9035 46.73%<span class="token punctuation">,</span> 0.8973 50.63%<span class="token punctuation">,</span> 0.8437 54.52%<span class="token punctuation">,</span> 0.7196 60.36%<span class="token punctuation">,</span> 0.6484 64.12%<span class="token punctuation">,</span> 0.5331 70.65%<span class="token punctuation">,</span> 0.4997 72.18%<span class="token punctuation">,</span> 0.4965 72.32%<span class="token punctuation">,</span> 0.4965 72.32%<span class="token punctuation">,</span> 0.4518 74.97%<span class="token punctuation">,</span> 0.4351 78.16%<span class="token punctuation">,</span> 0.4524 82.06%<span class="token punctuation">,</span> 0.491 86.51%<span class="token punctuation">,</span> 0.5539 91.66%<span class="token punctuation">,</span> 0.6377 95.97%<span class="token punctuation">,</span> 0.6928 99.17%<span class="token punctuation">,</span> 0.7014 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.8744 0%<span class="token punctuation">,</span> 0.8789 0.14%<span class="token punctuation">,</span> 0.8835 0.28%<span class="token punctuation">,</span> 0.8835 0.28%<span class="token punctuation">,</span> 0.9437 2.92%<span class="token punctuation">,</span> 0.9921 8.21%<span class="token punctuation">,</span> 0.9949 9.74%<span class="token punctuation">,</span> 0.9951 9.87%<span class="token punctuation">,</span> 0.0038 9.87%<span class="token punctuation">,</span> 0.0466 17.52%<span class="token punctuation">,</span> 0.0147 21.84%<span class="token punctuation">,</span> 0.0059 22.39%<span class="token punctuation">,</span> 0.0035 22.53%<span class="token punctuation">,</span> 0.9949 22.53%<span class="token punctuation">,</span> 0.8853 27.26%<span class="token punctuation">,</span> 0.7355 32.55%<span class="token punctuation">,</span> 0.5334 39.5%<span class="token punctuation">,</span> 0.4767 43.25%<span class="token punctuation">,</span> 0.4733 46.73%<span class="token punctuation">,</span> 0.5167 50.63%<span class="token punctuation">,</span> 0.5485 54.52%<span class="token punctuation">,</span> 0.6139 60.36%<span class="token punctuation">,</span> 0.708 64.12%<span class="token punctuation">,</span> 0.9398 70.65%<span class="token punctuation">,</span> 0.9915 72.18%<span class="token punctuation">,</span> 0.9959 72.32%<span class="token punctuation">,</span> 0.0045 72.32%<span class="token punctuation">,</span> 0.0662 74.97%<span class="token punctuation">,</span> 0.0886 78.16%<span class="token punctuation">,</span> 0.0816 82.06%<span class="token punctuation">,</span> 0.0846 86.51%<span class="token punctuation">,</span> 0.1228 91.66%<span class="token punctuation">,</span> 0.1981 95.97%<span class="token punctuation">,</span> 0.2795 99.17%<span class="token punctuation">,</span> 0.3011 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-17</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7027 0%<span class="token punctuation">,</span> 0.7169 3.2%<span class="token punctuation">,</span> 0.6933 6.95%<span class="token punctuation">,</span> 0.5864 13.63%<span class="token punctuation">,</span> 0.5592 16.97%<span class="token punctuation">,</span> 0.5604 20.72%<span class="token punctuation">,</span> 0.5847 25.17%<span class="token punctuation">,</span> 0.6209 29.35%<span class="token punctuation">,</span> 0.6732 33.24%<span class="token punctuation">,</span> 0.7528 37%<span class="token punctuation">,</span> 0.8336 40.89%<span class="token punctuation">,</span> 0.9299 46.45%<span class="token punctuation">,</span> 0.9671 50.9%<span class="token punctuation">,</span> 0.965 54.24%<span class="token punctuation">,</span> 0.9147 59.11%<span class="token punctuation">,</span> 0.8334 64.12%<span class="token punctuation">,</span> 0.8311 64.26%<span class="token punctuation">,</span> 0.8311 64.26%<span class="token punctuation">,</span> 0.7972 67.73%<span class="token punctuation">,</span> 0.7994 70.93%<span class="token punctuation">,</span> 0.8527 77.19%<span class="token punctuation">,</span> 0.9106 81.22%<span class="token punctuation">,</span> 0.9954 85.67%<span class="token punctuation">,</span> 0.9985 85.81%<span class="token punctuation">,</span> 0.005 85.81%<span class="token punctuation">,</span> 0.044 88.73%<span class="token punctuation">,</span> 0.0607 92.63%<span class="token punctuation">,</span> 0.0966 96.38%<span class="token punctuation">,</span> 0.1449 99.58%<span class="token punctuation">,</span> 0.151 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.3045 0%<span class="token punctuation">,</span> 0.3662 3.2%<span class="token punctuation">,</span> 0.4052 6.95%<span class="token punctuation">,</span> 0.4718 13.63%<span class="token punctuation">,</span> 0.5229 16.97%<span class="token punctuation">,</span> 0.55 20.72%<span class="token punctuation">,</span> 0.5259 25.17%<span class="token punctuation">,</span> 0.4619 29.35%<span class="token punctuation">,</span> 0.3968 33.24%<span class="token punctuation">,</span> 0.3763 37%<span class="token punctuation">,</span> 0.424 40.89%<span class="token punctuation">,</span> 0.531 46.45%<span class="token punctuation">,</span> 0.6381 50.9%<span class="token punctuation">,</span> 0.7346 54.24%<span class="token punctuation">,</span> 0.8695 59.11%<span class="token punctuation">,</span> 0.9931 64.12%<span class="token punctuation">,</span> 0.9971 64.26%<span class="token punctuation">,</span> 0.0057 64.26%<span class="token punctuation">,</span> 0.1001 67.73%<span class="token punctuation">,</span> 0.1726 70.93%<span class="token punctuation">,</span> 0.3122 77.19%<span class="token punctuation">,</span> 0.3952 81.22%<span class="token punctuation">,</span> 0.4356 85.67%<span class="token punctuation">,</span> 0.4364 85.81%<span class="token punctuation">,</span> 0.4364 85.81%<span class="token punctuation">,</span> 0.4403 88.73%<span class="token punctuation">,</span> 0.3991 92.63%<span class="token punctuation">,</span> 0.3132 96.38%<span class="token punctuation">,</span> 0.2621 99.58%<span class="token punctuation">,</span> 0.2595 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-18</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.3842 0%<span class="token punctuation">,</span> 0.3697 3.76%<span class="token punctuation">,</span> 0.2954 10.71%<span class="token punctuation">,</span> 0.224 14.19%<span class="token punctuation">,</span> 0.1445 17.66%<span class="token punctuation">,</span> 0.1028 20.86%<span class="token punctuation">,</span> 0.1037 23.92%<span class="token punctuation">,</span> 0.1845 32.27%<span class="token punctuation">,</span> 0.2344 38.11%<span class="token punctuation">,</span> 0.2512 42.28%<span class="token punctuation">,</span> 0.231 46.18%<span class="token punctuation">,</span> 0.2299 46.31%<span class="token punctuation">,</span> 0.2299 46.31%<span class="token punctuation">,</span> 0.2055 52.71%<span class="token punctuation">,</span> 0.1922 64.39%<span class="token punctuation">,</span> 0.2043 68.57%<span class="token punctuation">,</span> 0.2569 72.6%<span class="token punctuation">,</span> 0.3291 76.5%<span class="token punctuation">,</span> 0.3315 76.63%<span class="token punctuation">,</span> 0.3315 76.63%<span class="token punctuation">,</span> 0.4038 80.95%<span class="token punctuation">,</span> 0.5221 88.32%<span class="token punctuation">,</span> 0.6436 95.83%<span class="token punctuation">,</span> 0.674 99.58%<span class="token punctuation">,</span> 0.6753 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.2517 0%<span class="token punctuation">,</span> 0.2226 3.76%<span class="token punctuation">,</span> 0.0794 10.71%<span class="token punctuation">,</span> 0.0409 14.19%<span class="token punctuation">,</span> 0.061 17.66%<span class="token punctuation">,</span> 0.1217 20.86%<span class="token punctuation">,</span> 0.2083 23.92%<span class="token punctuation">,</span> 0.4859 32.27%<span class="token punctuation">,</span> 0.6965 38.11%<span class="token punctuation">,</span> 0.8527 42.28%<span class="token punctuation">,</span> 0.992 46.18%<span class="token punctuation">,</span> 0.9971 46.31%<span class="token punctuation">,</span> 0.0057 46.31%<span class="token punctuation">,</span> 0.2433 52.71%<span class="token punctuation">,</span> 0.6434 64.39%<span class="token punctuation">,</span> 0.7959 68.57%<span class="token punctuation">,</span> 0.8985 72.6%<span class="token punctuation">,</span> 0.9917 76.5%<span class="token punctuation">,</span> 0.9956 76.63%<span class="token punctuation">,</span> 0.0042 76.63%<span class="token punctuation">,</span> 0.0911 80.95%<span class="token punctuation">,</span> 0.1328 88.32%<span class="token punctuation">,</span> 0.2528 95.83%<span class="token punctuation">,</span> 0.322 99.58%<span class="token punctuation">,</span> 0.3275 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.667<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-19</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.6755 0%<span class="token punctuation">,</span> 0.6677 3.48%<span class="token punctuation">,</span> 0.6366 7.79%<span class="token punctuation">,</span> 0.5802 12.8%<span class="token punctuation">,</span> 0.5159 16.41%<span class="token punctuation">,</span> 0.4569 20.58%<span class="token punctuation">,</span> 0.45 24.06%<span class="token punctuation">,</span> 0.4799 28.51%<span class="token punctuation">,</span> 0.5397 32.27%<span class="token punctuation">,</span> 0.6565 37.27%<span class="token punctuation">,</span> 0.7548 41.03%<span class="token punctuation">,</span> 0.8957 47.43%<span class="token punctuation">,</span> 0.9708 51.74%<span class="token punctuation">,</span> 0.9967 54.66%<span class="token punctuation">,</span> 0.9973 54.8%<span class="token punctuation">,</span> 0.0039 54.8%<span class="token punctuation">,</span> 0.0041 57.3%<span class="token punctuation">,</span> 0.0036 57.44%<span class="token punctuation">,</span> 0.9971 57.44%<span class="token punctuation">,</span> 0.9609 61.2%<span class="token punctuation">,</span> 0.9024 64.67%<span class="token punctuation">,</span> 0.8299 68.57%<span class="token punctuation">,</span> 0.7484 72.88%<span class="token punctuation">,</span> 0.6534 76.5%<span class="token punctuation">,</span> 0.5564 80.39%<span class="token punctuation">,</span> 0.4229 86.09%<span class="token punctuation">,</span> 0.2396 92.91%<span class="token punctuation">,</span> 0.1144 98.05%<span class="token punctuation">,</span> 0.0849 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.3293 0%<span class="token punctuation">,</span> 0.3559 3.48%<span class="token punctuation">,</span> 0.3389 7.79%<span class="token punctuation">,</span> 0.2729 12.8%<span class="token punctuation">,</span> 0.2462 16.41%<span class="token punctuation">,</span> 0.2557 20.58%<span class="token punctuation">,</span> 0.2889 24.06%<span class="token punctuation">,</span> 0.3524 28.51%<span class="token punctuation">,</span> 0.421 32.27%<span class="token punctuation">,</span> 0.5185 37.27%<span class="token punctuation">,</span> 0.5763 41.03%<span class="token punctuation">,</span> 0.6301 47.43%<span class="token punctuation">,</span> 0.6944 51.74%<span class="token punctuation">,</span> 0.7569 54.66%<span class="token punctuation">,</span> 0.76 54.8%<span class="token punctuation">,</span> 0.76 54.8%<span class="token punctuation">,</span> 0.8051 57.3%<span class="token punctuation">,</span> 0.8072 57.44%<span class="token punctuation">,</span> 0.8072 57.44%<span class="token punctuation">,</span> 0.8408 61.2%<span class="token punctuation">,</span> 0.837 64.67%<span class="token punctuation">,</span> 0.7745 68.57%<span class="token punctuation">,</span> 0.6641 72.88%<span class="token punctuation">,</span> 0.6139 76.5%<span class="token punctuation">,</span> 0.6215 80.39%<span class="token punctuation">,</span> 0.6692 86.09%<span class="token punctuation">,</span> 0.7001 92.91%<span class="token punctuation">,</span> 0.7226 98.05%<span class="token punctuation">,</span> 0.7517 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-20</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.0835 0%<span class="token punctuation">,</span> 0.069 3.06%<span class="token punctuation">,</span> 0.0952 7.09%<span class="token punctuation">,</span> 0.1555 11.4%<span class="token punctuation">,</span> 0.1573 11.54%<span class="token punctuation">,</span> 0.1573 11.54%<span class="token punctuation">,</span> 0.1894 15.3%<span class="token punctuation">,</span> 0.1848 20.72%<span class="token punctuation">,</span> 0.1891 26.15%<span class="token punctuation">,</span> 0.159 29.49%<span class="token punctuation">,</span> 0.1049 34.08%<span class="token punctuation">,</span> 0.0585 38.11%<span class="token punctuation">,</span> 0.0056 42%<span class="token punctuation">,</span> 0.0033 42.14%<span class="token punctuation">,</span> 0.9968 42.14%<span class="token punctuation">,</span> 0.968 45.06%<span class="token punctuation">,</span> 0.9773 49.65%<span class="token punctuation">,</span> 0.9965 51.74%<span class="token punctuation">,</span> 0.9982 51.88%<span class="token punctuation">,</span> 0.0047 51.88%<span class="token punctuation">,</span> 0.0275 55.91%<span class="token punctuation">,</span> 0.0039 60.22%<span class="token punctuation">,</span> 0.0026 60.36%<span class="token punctuation">,</span> 0.9961 60.36%<span class="token punctuation">,</span> 0.9454 65.51%<span class="token punctuation">,</span> 0.8793 69.54%<span class="token punctuation">,</span> 0.8275 73.02%<span class="token punctuation">,</span> 0.695 80.39%<span class="token punctuation">,</span> 0.5874 85.12%<span class="token punctuation">,</span> 0.488 89.43%<span class="token punctuation">,</span> 0.3361 96.52%<span class="token punctuation">,</span> 0.2513 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7541 0%<span class="token punctuation">,</span> 0.8222 3.06%<span class="token punctuation">,</span> 0.9038 7.09%<span class="token punctuation">,</span> 0.9928 11.4%<span class="token punctuation">,</span> 0.9969 11.54%<span class="token punctuation">,</span> 0.0055 11.54%<span class="token punctuation">,</span> 0.1287 15.3%<span class="token punctuation">,</span> 0.3454 20.72%<span class="token punctuation">,</span> 0.6056 26.15%<span class="token punctuation">,</span> 0.7317 29.49%<span class="token punctuation">,</span> 0.8331 34.08%<span class="token punctuation">,</span> 0.8832 38.11%<span class="token punctuation">,</span> 0.9111 42%<span class="token punctuation">,</span> 0.912 42.14%<span class="token punctuation">,</span> 0.912 42.14%<span class="token punctuation">,</span> 0.9081 45.06%<span class="token punctuation">,</span> 0.8629 49.65%<span class="token punctuation">,</span> 0.8403 51.74%<span class="token punctuation">,</span> 0.8389 51.88%<span class="token punctuation">,</span> 0.8389 51.88%<span class="token punctuation">,</span> 0.7831 55.91%<span class="token punctuation">,</span> 0.7218 60.22%<span class="token punctuation">,</span> 0.7198 60.36%<span class="token punctuation">,</span> 0.7198 60.36%<span class="token punctuation">,</span> 0.6186 65.51%<span class="token punctuation">,</span> 0.5484 69.54%<span class="token punctuation">,</span> 0.5427 73.02%<span class="token punctuation">,</span> 0.6274 80.39%<span class="token punctuation">,</span> 0.6538 85.12%<span class="token punctuation">,</span> 0.6064 89.43%<span class="token punctuation">,</span> 0.4458 96.52%<span class="token punctuation">,</span> 0.3535 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-21</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4218 0%<span class="token punctuation">,</span> 0.4293 4.59%<span class="token punctuation">,</span> 0.4606 8.21%<span class="token punctuation">,</span> 0.5662 16.41%<span class="token punctuation">,</span> 0.7005 24.2%<span class="token punctuation">,</span> 0.8248 30.6%<span class="token punctuation">,</span> 0.9155 35.61%<span class="token punctuation">,</span> 0.9947 39.64%<span class="token punctuation">,</span> 0.9974 39.78%<span class="token punctuation">,</span> 0.0039 39.78%<span class="token punctuation">,</span> 0.0384 42.56%<span class="token punctuation">,</span> 0.0429 46.18%<span class="token punctuation">,</span> 0.0097 50.35%<span class="token punctuation">,</span> 0.0046 50.76%<span class="token punctuation">,</span> 0.003 50.9%<span class="token punctuation">,</span> 0.9965 50.9%<span class="token punctuation">,</span> 0.9174 55.49%<span class="token punctuation">,</span> 0.8532 58.97%<span class="token punctuation">,</span> 0.8203 62.73%<span class="token punctuation">,</span> 0.8357 67.73%<span class="token punctuation">,</span> 0.8549 72.46%<span class="token punctuation">,</span> 0.8397 76.36%<span class="token punctuation">,</span> 0.7905 83.31%<span class="token punctuation">,</span> 0.7518 87.62%<span class="token punctuation">,</span> 0.7082 90.96%<span class="token punctuation">,</span> 0.6353 96.11%<span class="token punctuation">,</span> 0.5685 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.8963 0%<span class="token punctuation">,</span> 0.8993 4.59%<span class="token punctuation">,</span> 0.8749 8.21%<span class="token punctuation">,</span> 0.7191 16.41%<span class="token punctuation">,</span> 0.5566 24.2%<span class="token punctuation">,</span> 0.4528 30.6%<span class="token punctuation">,</span> 0.4191 35.61%<span class="token punctuation">,</span> 0.4447 39.64%<span class="token punctuation">,</span> 0.4459 39.78%<span class="token punctuation">,</span> 0.4459 39.78%<span class="token punctuation">,</span> 0.4545 42.56%<span class="token punctuation">,</span> 0.4434 46.18%<span class="token punctuation">,</span> 0.4129 50.35%<span class="token punctuation">,</span> 0.4095 50.76%<span class="token punctuation">,</span> 0.4085 50.9%<span class="token punctuation">,</span> 0.4085 50.9%<span class="token punctuation">,</span> 0.3578 55.49%<span class="token punctuation">,</span> 0.3556 58.97%<span class="token punctuation">,</span> 0.3862 62.73%<span class="token punctuation">,</span> 0.4598 67.73%<span class="token punctuation">,</span> 0.558 72.46%<span class="token punctuation">,</span> 0.6237 76.36%<span class="token punctuation">,</span> 0.7301 83.31%<span class="token punctuation">,</span> 0.7437 87.62%<span class="token punctuation">,</span> 0.7161 90.96%<span class="token punctuation">,</span> 0.6302 96.11%<span class="token punctuation">,</span> 0.5213 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.667<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-22</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5662 0%<span class="token punctuation">,</span> 0.4508 7.65%<span class="token punctuation">,</span> 0.3678 13.63%<span class="token punctuation">,</span> 0.3468 14.74%<span class="token punctuation">,</span> 0.3439 14.88%<span class="token punctuation">,</span> 0.3439 14.88%<span class="token punctuation">,</span> 0.2926 17.94%<span class="token punctuation">,</span> 0.2468 21.28%<span class="token punctuation">,</span> 0.2068 24.48%<span class="token punctuation">,</span> 0.2053 24.62%<span class="token punctuation">,</span> 0.2053 24.62%<span class="token punctuation">,</span> 0.192 28.23%<span class="token punctuation">,</span> 0.2149 31.71%<span class="token punctuation">,</span> 0.266 34.91%<span class="token punctuation">,</span> 0.4053 40.75%<span class="token punctuation">,</span> 0.5222 45.06%<span class="token punctuation">,</span> 0.5949 48.26%<span class="token punctuation">,</span> 0.5972 48.4%<span class="token punctuation">,</span> 0.5994 48.54%<span class="token punctuation">,</span> 0.5994 48.54%<span class="token punctuation">,</span> 0.6374 51.46%<span class="token punctuation">,</span> 0.6729 54.52%<span class="token punctuation">,</span> 0.6747 54.66%<span class="token punctuation">,</span> 0.6747 54.66%<span class="token punctuation">,</span> 0.6936 58%<span class="token punctuation">,</span> 0.6789 62.45%<span class="token punctuation">,</span> 0.6555 66.9%<span class="token punctuation">,</span> 0.6572 70.65%<span class="token punctuation">,</span> 0.6823 73.44%<span class="token punctuation">,</span> 0.726 77.19%<span class="token punctuation">,</span> 0.7594 81.22%<span class="token punctuation">,</span> 0.8044 85.26%<span class="token punctuation">,</span> 0.8678 89.57%<span class="token punctuation">,</span> 0.9864 95.41%<span class="token punctuation">,</span> 0.997 95.97%<span class="token punctuation">,</span> 0.9995 96.11%<span class="token punctuation">,</span> 0.0061 96.11%<span class="token punctuation">,</span> 0.0773 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5166 0%<span class="token punctuation">,</span> 0.2522 7.65%<span class="token punctuation">,</span> 0.0396 13.63%<span class="token punctuation">,</span> 0.0066 14.74%<span class="token punctuation">,</span> 0.0029 14.88%<span class="token punctuation">,</span> 0.9943 14.88%<span class="token punctuation">,</span> 0.9468 17.94%<span class="token punctuation">,</span> 0.9559 21.28%<span class="token punctuation">,</span> 0.9932 24.48%<span class="token punctuation">,</span> 0.9954 24.62%<span class="token punctuation">,</span> 0.0041 24.62%<span class="token punctuation">,</span> 0.0576 28.23%<span class="token punctuation">,</span> 0.1062 31.71%<span class="token punctuation">,</span> 0.113 34.91%<span class="token punctuation">,</span> 0.1112 40.75%<span class="token punctuation">,</span> 0.0837 45.06%<span class="token punctuation">,</span> 0.0103 48.26%<span class="token punctuation">,</span> 0.0067 48.4%<span class="token punctuation">,</span> 0.0033 48.54%<span class="token punctuation">,</span> 0.9946 48.54%<span class="token punctuation">,</span> 0.9629 51.46%<span class="token punctuation">,</span> 0.9944 54.52%<span class="token punctuation">,</span> 0.9967 54.66%<span class="token punctuation">,</span> 0.0054 54.66%<span class="token punctuation">,</span> 0.0506 58%<span class="token punctuation">,</span> 0.0914 62.45%<span class="token punctuation">,</span> 0.0667 66.9%<span class="token punctuation">,</span> 0.0192 70.65%<span class="token punctuation">,</span> 0.0096 73.44%<span class="token punctuation">,</span> 0.0497 77.19%<span class="token punctuation">,</span> 0.1495 81.22%<span class="token punctuation">,</span> 0.2511 85.26%<span class="token punctuation">,</span> 0.2962 89.57%<span class="token punctuation">,</span> 0.3747 95.41%<span class="token punctuation">,</span> 0.3852 95.97%<span class="token punctuation">,</span> 0.3878 96.11%<span class="token punctuation">,</span> 0.3878 96.11%<span class="token punctuation">,</span> 0.458 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-23</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.0794 0%<span class="token punctuation">,</span> 0.1011 2.78%<span class="token punctuation">,</span> 0.1215 6.4%<span class="token punctuation">,</span> 0.1716 10.29%<span class="token punctuation">,</span> 0.2315 13.35%<span class="token punctuation">,</span> 0.3705 19.19%<span class="token punctuation">,</span> 0.4347 22.95%<span class="token punctuation">,</span> 0.4366 23.09%<span class="token punctuation">,</span> 0.4366 23.09%<span class="token punctuation">,</span> 0.4756 27.12%<span class="token punctuation">,</span> 0.4856 31.29%<span class="token punctuation">,</span> 0.4954 37.13%<span class="token punctuation">,</span> 0.4836 42.28%<span class="token punctuation">,</span> 0.4488 45.76%<span class="token punctuation">,</span> 0.3985 48.54%<span class="token punctuation">,</span> 0.3957 48.68%<span class="token punctuation">,</span> 0.3957 48.68%<span class="token punctuation">,</span> 0.3623 51.32%<span class="token punctuation">,</span> 0.3555 54.66%<span class="token punctuation">,</span> 0.3727 58.55%<span class="token punctuation">,</span> 0.3605 62.31%<span class="token punctuation">,</span> 0.3059 67.18%<span class="token punctuation">,</span> 0.2117 73.71%<span class="token punctuation">,</span> 0.1206 78.58%<span class="token punctuation">,</span> 0.0059 84.7%<span class="token punctuation">,</span> 0.0033 84.84%<span class="token punctuation">,</span> 0.9967 84.84%<span class="token punctuation">,</span> 0.964 87.62%<span class="token punctuation">,</span> 0.963 91.24%<span class="token punctuation">,</span> 0.9858 95.97%<span class="token punctuation">,</span> 0.9894 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4607 0%<span class="token punctuation">,</span> 0.5286 2.78%<span class="token punctuation">,</span> 0.6448 6.4%<span class="token punctuation">,</span> 0.7497 10.29%<span class="token punctuation">,</span> 0.7926 13.35%<span class="token punctuation">,</span> 0.885 19.19%<span class="token punctuation">,</span> 0.9912 22.95%<span class="token punctuation">,</span> 0.9956 23.09%<span class="token punctuation">,</span> 0.0042 23.09%<span class="token punctuation">,</span> 0.1186 27.12%<span class="token punctuation">,</span> 0.2515 31.29%<span class="token punctuation">,</span> 0.4987 37.13%<span class="token punctuation">,</span> 0.7434 42.28%<span class="token punctuation">,</span> 0.8935 45.76%<span class="token punctuation">,</span> 0.9949 48.54%<span class="token punctuation">,</span> 0.9997 48.68%<span class="token punctuation">,</span> 0.0083 48.68%<span class="token punctuation">,</span> 0.0756 51.32%<span class="token punctuation">,</span> 0.1122 54.66%<span class="token punctuation">,</span> 0.1198 58.55%<span class="token punctuation">,</span> 0.0987 62.31%<span class="token punctuation">,</span> 0.0879 67.18%<span class="token punctuation">,</span> 0.1178 73.71%<span class="token punctuation">,</span> 0.1912 78.58%<span class="token punctuation">,</span> 0.3378 84.7%<span class="token punctuation">,</span> 0.3404 84.84%<span class="token punctuation">,</span> 0.3404 84.84%<span class="token punctuation">,</span> 0.3749 87.62%<span class="token punctuation">,</span> 0.4188 91.24%<span class="token punctuation">,</span> 0.513 95.97%<span class="token punctuation">,</span> 0.6261 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-24</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.9865 0%<span class="token punctuation">,</span> 0.9855 9.6%<span class="token punctuation">,</span> 0.9501 15.86%<span class="token punctuation">,</span> 0.9021 21.56%<span class="token punctuation">,</span> 0.8532 27.68%<span class="token punctuation">,</span> 0.8445 32.13%<span class="token punctuation">,</span> 0.8668 36.16%<span class="token punctuation">,</span> 0.9219 41.03%<span class="token punctuation">,</span> 0.9756 44.51%<span class="token punctuation">,</span> 0.9956 45.9%<span class="token punctuation">,</span> 0.9974 46.04%<span class="token punctuation">,</span> 0.004 46.04%<span class="token punctuation">,</span> 0.0261 49.24%<span class="token punctuation">,</span> 0.0046 54.38%<span class="token punctuation">,</span> 0.0036 54.52%<span class="token punctuation">,</span> 0.9971 54.52%<span class="token punctuation">,</span> 0.9489 59.25%<span class="token punctuation">,</span> 0.8773 63%<span class="token punctuation">,</span> 0.8153 67.45%<span class="token punctuation">,</span> 0.7417 72.88%<span class="token punctuation">,</span> 0.6547 77.05%<span class="token punctuation">,</span> 0.5042 82.89%<span class="token punctuation">,</span> 0.22 93.32%<span class="token punctuation">,</span> 0.1051 97.64%<span class="token punctuation">,</span> 0.0512 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.9439 0%<span class="token punctuation">,</span> 0.8888 9.6%<span class="token punctuation">,</span> 0.8947 15.86%<span class="token punctuation">,</span> 0.8721 21.56%<span class="token punctuation">,</span> 0.814 27.68%<span class="token punctuation">,</span> 0.7205 32.13%<span class="token punctuation">,</span> 0.6136 36.16%<span class="token punctuation">,</span> 0.4999 41.03%<span class="token punctuation">,</span> 0.4829 44.51%<span class="token punctuation">,</span> 0.493 45.9%<span class="token punctuation">,</span> 0.4943 46.04%<span class="token punctuation">,</span> 0.4943 46.04%<span class="token punctuation">,</span> 0.5111 49.24%<span class="token punctuation">,</span> 0.5063 54.38%<span class="token punctuation">,</span> 0.5061 54.52%<span class="token punctuation">,</span> 0.5061 54.52%<span class="token punctuation">,</span> 0.4679 59.25%<span class="token punctuation">,</span> 0.4706 63%<span class="token punctuation">,</span> 0.5461 67.45%<span class="token punctuation">,</span> 0.6819 72.88%<span class="token punctuation">,</span> 0.7479 77.05%<span class="token punctuation">,</span> 0.7511 82.89%<span class="token punctuation">,</span> 0.6755 93.32%<span class="token punctuation">,</span> 0.6712 97.64%<span class="token punctuation">,</span> 0.6968 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.667<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-25</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.0484 0%<span class="token punctuation">,</span> 0.0042 2.92%<span class="token punctuation">,</span> 0.0029 3.06%<span class="token punctuation">,</span> 0.9964 3.06%<span class="token punctuation">,</span> 0.9826 5.84%<span class="token punctuation">,</span> 0.9418 14.6%<span class="token punctuation">,</span> 0.8946 21.56%<span class="token punctuation">,</span> 0.8557 26.29%<span class="token punctuation">,</span> 0.8217 30.88%<span class="token punctuation">,</span> 0.8276 34.49%<span class="token punctuation">,</span> 0.8797 39.08%<span class="token punctuation">,</span> 0.9251 42.84%<span class="token punctuation">,</span> 0.9551 46.45%<span class="token punctuation">,</span> 0.9422 50.76%<span class="token punctuation">,</span> 0.8641 56.05%<span class="token punctuation">,</span> 0.7693 60.78%<span class="token punctuation">,</span> 0.6516 67.04%<span class="token punctuation">,</span> 0.5701 72.18%<span class="token punctuation">,</span> 0.476 76.36%<span class="token punctuation">,</span> 0.4085 79%<span class="token punctuation">,</span> 0.4047 79.14%<span class="token punctuation">,</span> 0.4047 79.14%<span class="token punctuation">,</span> 0.3461 82.2%<span class="token punctuation">,</span> 0.3246 85.26%<span class="token punctuation">,</span> 0.3364 90.4%<span class="token punctuation">,</span> 0.3799 94.58%<span class="token punctuation">,</span> 0.4443 99.03%<span class="token punctuation">,</span> 0.4569 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.6987 0%<span class="token punctuation">,</span> 0.7533 2.92%<span class="token punctuation">,</span> 0.7564 3.06%<span class="token punctuation">,</span> 0.7564 3.06%<span class="token punctuation">,</span> 0.796 5.84%<span class="token punctuation">,</span> 0.8036 14.6%<span class="token punctuation">,</span> 0.8193 21.56%<span class="token punctuation">,</span> 0.7723 26.29%<span class="token punctuation">,</span> 0.6819 30.88%<span class="token punctuation">,</span> 0.5939 34.49%<span class="token punctuation">,</span> 0.4877 39.08%<span class="token punctuation">,</span> 0.4537 42.84%<span class="token punctuation">,</span> 0.4613 46.45%<span class="token punctuation">,</span> 0.4974 50.76%<span class="token punctuation">,</span> 0.5286 56.05%<span class="token punctuation">,</span> 0.5787 60.78%<span class="token punctuation">,</span> 0.7221 67.04%<span class="token punctuation">,</span> 0.8793 72.18%<span class="token punctuation">,</span> 0.9544 76.36%<span class="token punctuation">,</span> 0.9945 79%<span class="token punctuation">,</span> 0.9967 79.14%<span class="token punctuation">,</span> 0.0053 79.14%<span class="token punctuation">,</span> 0.0466 82.2%<span class="token punctuation">,</span> 0.0604 85.26%<span class="token punctuation">,</span> 0.0613 90.4%<span class="token punctuation">,</span> 0.0422 94.58%<span class="token punctuation">,</span> 0.0857 99.03%<span class="token punctuation">,</span> 0.1038 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-26</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4583 0%<span class="token punctuation">,</span> 0.4718 3.48%<span class="token punctuation">,</span> 0.4675 6.95%<span class="token punctuation">,</span> 0.4465 11.27%<span class="token punctuation">,</span> 0.4137 14.19%<span class="token punctuation">,</span> 0.4114 14.33%<span class="token punctuation">,</span> 0.4114 14.33%<span class="token punctuation">,</span> 0.3905 16.83%<span class="token punctuation">,</span> 0.3968 20.03%<span class="token punctuation">,</span> 0.4125 23.09%<span class="token punctuation">,</span> 0.4164 24.62%<span class="token punctuation">,</span> 0.4166 24.76%<span class="token punctuation">,</span> 0.4166 24.76%<span class="token punctuation">,</span> 0.4127 28.79%<span class="token punctuation">,</span> 0.3992 36.16%<span class="token punctuation">,</span> 0.3719 44.78%<span class="token punctuation">,</span> 0.3393 47.98%<span class="token punctuation">,</span> 0.3377 48.12%<span class="token punctuation">,</span> 0.3377 48.12%<span class="token punctuation">,</span> 0.3135 51.88%<span class="token punctuation">,</span> 0.3274 55.77%<span class="token punctuation">,</span> 0.3802 59.67%<span class="token punctuation">,</span> 0.4431 62.87%<span class="token punctuation">,</span> 0.4983 65.79%<span class="token punctuation">,</span> 0.5317 68.71%<span class="token punctuation">,</span> 0.5871 74.69%<span class="token punctuation">,</span> 0.6439 78.16%<span class="token punctuation">,</span> 0.6916 81.5%<span class="token punctuation">,</span> 0.7213 85.95%<span class="token punctuation">,</span> 0.7168 89.71%<span class="token punctuation">,</span> 0.676 93.32%<span class="token punctuation">,</span> 0.621 97.08%<span class="token punctuation">,</span> 0.5736 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.1065 0%<span class="token punctuation">,</span> 0.1681 3.48%<span class="token punctuation">,</span> 0.1627 6.95%<span class="token punctuation">,</span> 0.0853 11.27%<span class="token punctuation">,</span> 0.0063 14.19%<span class="token punctuation">,</span> 0.0025 14.33%<span class="token punctuation">,</span> 0.9939 14.33%<span class="token punctuation">,</span> 0.9465 16.83%<span class="token punctuation">,</span> 0.9338 20.03%<span class="token punctuation">,</span> 0.9583 23.09%<span class="token punctuation">,</span> 0.994 24.62%<span class="token punctuation">,</span> 0.9977 24.76%<span class="token punctuation">,</span> 0.0063 24.76%<span class="token punctuation">,</span> 0.1389 28.79%<span class="token punctuation">,</span> 0.4508 36.16%<span class="token punctuation">,</span> 0.8565 44.78%<span class="token punctuation">,</span> 0.9901 47.98%<span class="token punctuation">,</span> 0.9957 48.12%<span class="token punctuation">,</span> 0.0043 48.12%<span class="token punctuation">,</span> 0.1513 51.88%<span class="token punctuation">,</span> 0.311 55.77%<span class="token punctuation">,</span> 0.4588 59.67%<span class="token punctuation">,</span> 0.5368 62.87%<span class="token punctuation">,</span> 0.5603 65.79%<span class="token punctuation">,</span> 0.5375 68.71%<span class="token punctuation">,</span> 0.4435 74.69%<span class="token punctuation">,</span> 0.4163 78.16%<span class="token punctuation">,</span> 0.4542 81.5%<span class="token punctuation">,</span> 0.5314 85.95%<span class="token punctuation">,</span> 0.6122 89.71%<span class="token punctuation">,</span> 0.6598 93.32%<span class="token punctuation">,</span> 0.651 97.08%<span class="token punctuation">,</span> 0.6157 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-27</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7128 0%<span class="token punctuation">,</span> 0.7177 4.87%<span class="token punctuation">,</span> 0.6806 9.46%<span class="token punctuation">,</span> 0.6408 13.21%<span class="token punctuation">,</span> 0.6076 17.11%<span class="token punctuation">,</span> 0.6064 17.25%<span class="token punctuation">,</span> 0.6064 17.25%<span class="token punctuation">,</span> 0.5894 21.14%<span class="token punctuation">,</span> 0.6117 23.92%<span class="token punctuation">,</span> 0.7116 29.21%<span class="token punctuation">,</span> 0.8415 35.33%<span class="token punctuation">,</span> 0.9125 39.64%<span class="token punctuation">,</span> 0.98 43.81%<span class="token punctuation">,</span> 0.9967 45.62%<span class="token punctuation">,</span> 0.9973 45.76%<span class="token punctuation">,</span> 0.0038 45.76%<span class="token punctuation">,</span> 0.0047 50.63%<span class="token punctuation">,</span> 0.004 50.9%<span class="token punctuation">,</span> 0.0036 51.04%<span class="token punctuation">,</span> 0.9971 51.04%<span class="token punctuation">,</span> 0.968 55.22%<span class="token punctuation">,</span> 0.9115 58.83%<span class="token punctuation">,</span> 0.8409 62.73%<span class="token punctuation">,</span> 0.8142 65.51%<span class="token punctuation">,</span> 0.7868 70.51%<span class="token punctuation">,</span> 0.7514 73.44%<span class="token punctuation">,</span> 0.6886 76.77%<span class="token punctuation">,</span> 0.5421 83.31%<span class="token punctuation">,</span> 0.44 87.9%<span class="token punctuation">,</span> 0.2643 94.44%<span class="token punctuation">,</span> 0.1193 99.86%<span class="token punctuation">,</span> 0.1161 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.0662 0%<span class="token punctuation">,</span> 0.0866 4.87%<span class="token punctuation">,</span> 0.1144 9.46%<span class="token punctuation">,</span> 0.0892 13.21%<span class="token punctuation">,</span> 0.0038 17.11%<span class="token punctuation">,</span> 0 17.25%<span class="token punctuation">,</span> 0.9914 17.25%<span class="token punctuation">,</span> 0.8809 21.14%<span class="token punctuation">,</span> 0.82 23.92%<span class="token punctuation">,</span> 0.7252 29.21%<span class="token punctuation">,</span> 0.5797 35.33%<span class="token punctuation">,</span> 0.5011 39.64%<span class="token punctuation">,</span> 0.4734 43.81%<span class="token punctuation">,</span> 0.4827 45.62%<span class="token punctuation">,</span> 0.4838 45.76%<span class="token punctuation">,</span> 0.4838 45.76%<span class="token punctuation">,</span> 0.5044 50.63%<span class="token punctuation">,</span> 0.5052 50.9%<span class="token punctuation">,</span> 0.5055 51.04%<span class="token punctuation">,</span> 0.5055 51.04%<span class="token punctuation">,</span> 0.4769 55.22%<span class="token punctuation">,</span> 0.4393 58.83%<span class="token punctuation">,</span> 0.4584 62.73%<span class="token punctuation">,</span> 0.5067 65.51%<span class="token punctuation">,</span> 0.6581 70.51%<span class="token punctuation">,</span> 0.7193 73.44%<span class="token punctuation">,</span> 0.7384 76.77%<span class="token punctuation">,</span> 0.6963 83.31%<span class="token punctuation">,</span> 0.6257 87.9%<span class="token punctuation">,</span> 0.5415 94.44%<span class="token punctuation">,</span> 0.523 99.86%<span class="token punctuation">,</span> 0.5233 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.667<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-28</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.1129 0%<span class="token punctuation">,</span> 0.0244 3.76%<span class="token punctuation">,</span> 0.0052 4.73%<span class="token punctuation">,</span> 0.0026 4.87%<span class="token punctuation">,</span> 0.9961 4.87%<span class="token punctuation">,</span> 0.9607 8.34%<span class="token punctuation">,</span> 0.9398 12.24%<span class="token punctuation">,</span> 0.9083 17.66%<span class="token punctuation">,</span> 0.9081 21.14%<span class="token punctuation">,</span> 0.9436 24.76%<span class="token punctuation">,</span> 0.9964 29.07%<span class="token punctuation">,</span> 0.9986 29.21%<span class="token punctuation">,</span> 0.0052 29.21%<span class="token punctuation">,</span> 0.1087 34.77%<span class="token punctuation">,</span> 0.1959 39.22%<span class="token punctuation">,</span> 0.2779 43.53%<span class="token punctuation">,</span> 0.3643 48.4%<span class="token punctuation">,</span> 0.4578 52.57%<span class="token punctuation">,</span> 0.5287 55.77%<span class="token punctuation">,</span> 0.5658 58.97%<span class="token punctuation">,</span> 0.5629 62.73%<span class="token punctuation">,</span> 0.5323 69.82%<span class="token punctuation">,</span> 0.5457 74.13%<span class="token punctuation">,</span> 0.5549 75.24%<span class="token punctuation">,</span> 0.5563 75.38%<span class="token punctuation">,</span> 0.5563 75.38%<span class="token punctuation">,</span> 0.5668 78.72%<span class="token punctuation">,</span> 0.5363 83.59%<span class="token punctuation">,</span> 0.4908 89.15%<span class="token punctuation">,</span> 0.4482 94.85%<span class="token punctuation">,</span> 0.4124 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5235 0%<span class="token punctuation">,</span> 0.5491 3.76%<span class="token punctuation">,</span> 0.5659 4.73%<span class="token punctuation">,</span> 0.5687 4.87%<span class="token punctuation">,</span> 0.5687 4.87%<span class="token punctuation">,</span> 0.6235 8.34%<span class="token punctuation">,</span> 0.629 12.24%<span class="token punctuation">,</span> 0.5348 17.66%<span class="token punctuation">,</span> 0.472 21.14%<span class="token punctuation">,</span> 0.4502 24.76%<span class="token punctuation">,</span> 0.4654 29.07%<span class="token punctuation">,</span> 0.4663 29.21%<span class="token punctuation">,</span> 0.4663 29.21%<span class="token punctuation">,</span> 0.4769 34.77%<span class="token punctuation">,</span> 0.4456 39.22%<span class="token punctuation">,</span> 0.3601 43.53%<span class="token punctuation">,</span> 0.2538 48.4%<span class="token punctuation">,</span> 0.2248 52.57%<span class="token punctuation">,</span> 0.1856 55.77%<span class="token punctuation">,</span> 0.1265 58.97%<span class="token punctuation">,</span> 0.0855 62.73%<span class="token punctuation">,</span> 0.0551 69.82%<span class="token punctuation">,</span> 0.0098 74.13%<span class="token punctuation">,</span> 0.0036 75.24%<span class="token punctuation">,</span> 0.0032 75.38%<span class="token punctuation">,</span> 0.9945 75.38%<span class="token punctuation">,</span> 0.9729 78.72%<span class="token punctuation">,</span> 0.9216 83.59%<span class="token punctuation">,</span> 0.7923 89.15%<span class="token punctuation">,</span> 0.5968 94.85%<span class="token punctuation">,</span> 0.396 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-29</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4115 0%<span class="token punctuation">,</span> 0.363 6.54%<span class="token punctuation">,</span> 0.3132 10.01%<span class="token punctuation">,</span> 0.311 10.15%<span class="token punctuation">,</span> 0.311 10.15%<span class="token punctuation">,</span> 0.2927 12.66%<span class="token punctuation">,</span> 0.3019 16.69%<span class="token punctuation">,</span> 0.3077 21.14%<span class="token punctuation">,</span> 0.305 22.11%<span class="token punctuation">,</span> 0.3044 22.25%<span class="token punctuation">,</span> 0.3044 22.25%<span class="token punctuation">,</span> 0.2901 25.31%<span class="token punctuation">,</span> 0.2824 31.15%<span class="token punctuation">,</span> 0.2832 39.5%<span class="token punctuation">,</span> 0.2998 45.62%<span class="token punctuation">,</span> 0.2971 47.98%<span class="token punctuation">,</span> 0.2968 48.12%<span class="token punctuation">,</span> 0.2968 48.12%<span class="token punctuation">,</span> 0.298 53.27%<span class="token punctuation">,</span> 0.3213 57.16%<span class="token punctuation">,</span> 0.3741 60.92%<span class="token punctuation">,</span> 0.4245 64.26%<span class="token punctuation">,</span> 0.4505 67.18%<span class="token punctuation">,</span> 0.4453 71.91%<span class="token punctuation">,</span> 0.4046 79%<span class="token punctuation">,</span> 0.3769 83.45%<span class="token punctuation">,</span> 0.3839 88.04%<span class="token punctuation">,</span> 0.4156 92.07%<span class="token punctuation">,</span> 0.4643 95.83%<span class="token punctuation">,</span> 0.5032 99.17%<span class="token punctuation">,</span> 0.51 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.3903 0%<span class="token punctuation">,</span> 0.1259 6.54%<span class="token punctuation">,</span> 0.0064 10.01%<span class="token punctuation">,</span> 0.0028 10.15%<span class="token punctuation">,</span> 0.9942 10.15%<span class="token punctuation">,</span> 0.9447 12.66%<span class="token punctuation">,</span> 0.9261 16.69%<span class="token punctuation">,</span> 0.9762 21.14%<span class="token punctuation">,</span> 0.9945 22.11%<span class="token punctuation">,</span> 0.9972 22.25%<span class="token punctuation">,</span> 0.0058 22.25%<span class="token punctuation">,</span> 0.0912 25.31%<span class="token punctuation">,</span> 0.3343 31.15%<span class="token punctuation">,</span> 0.6731 39.5%<span class="token punctuation">,</span> 0.8995 45.62%<span class="token punctuation">,</span> 0.9942 47.98%<span class="token punctuation">,</span> 1 48.12%<span class="token punctuation">,</span> 0.0086 48.12%<span class="token punctuation">,</span> 0.1933 53.27%<span class="token punctuation">,</span> 0.3227 57.16%<span class="token punctuation">,</span> 0.4457 60.92%<span class="token punctuation">,</span> 0.5038 64.26%<span class="token punctuation">,</span> 0.5049 67.18%<span class="token punctuation">,</span> 0.46 71.91%<span class="token punctuation">,</span> 0.367 79%<span class="token punctuation">,</span> 0.2778 83.45%<span class="token punctuation">,</span> 0.1509 88.04%<span class="token punctuation">,</span> 0.0652 92.07%<span class="token punctuation">,</span> 0.0443 95.83%<span class="token punctuation">,</span> 0.0837 99.17%<span class="token punctuation">,</span> 0.0994 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-30</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4642 0%<span class="token punctuation">,</span> 0.4366 3.48%<span class="token punctuation">,</span> 0.3586 7.65%<span class="token punctuation">,</span> 0.2629 12.1%<span class="token punctuation">,</span> 0.2012 15.3%<span class="token punctuation">,</span> 0.135 19.19%<span class="token punctuation">,</span> 0.1058 22.25%<span class="token punctuation">,</span> 0.1131 26.01%<span class="token punctuation">,</span> 0.1783 33.24%<span class="token punctuation">,</span> 0.1969 39.22%<span class="token punctuation">,</span> 0.1883 44.23%<span class="token punctuation">,</span> 0.1711 48.54%<span class="token punctuation">,</span> 0.1706 48.68%<span class="token punctuation">,</span> 0.1706 48.68%<span class="token punctuation">,</span> 0.1672 55.35%<span class="token punctuation">,</span> 0.1961 60.92%<span class="token punctuation">,</span> 0.233 64.67%<span class="token punctuation">,</span> 0.273 68.43%<span class="token punctuation">,</span> 0.2736 72.04%<span class="token punctuation">,</span> 0.23 76.77%<span class="token punctuation">,</span> 0.1626 84.84%<span class="token punctuation">,</span> 0.1351 88.46%<span class="token punctuation">,</span> 0.134 93.05%<span class="token punctuation">,</span> 0.1613 96.38%<span class="token punctuation">,</span> 0.1774 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.1471 0%<span class="token punctuation">,</span> 0.1409 3.48%<span class="token punctuation">,</span> 0.0859 7.65%<span class="token punctuation">,</span> 0.0264 12.1%<span class="token punctuation">,</span> 0.0395 15.3%<span class="token punctuation">,</span> 0.1166 19.19%<span class="token punctuation">,</span> 0.2062 22.25%<span class="token punctuation">,</span> 0.334 26.01%<span class="token punctuation">,</span> 0.5861 33.24%<span class="token punctuation">,</span> 0.7738 39.22%<span class="token punctuation">,</span> 0.9095 44.23%<span class="token punctuation">,</span> 0.9941 48.54%<span class="token punctuation">,</span> 0.9969 48.68%<span class="token punctuation">,</span> 0.0055 48.68%<span class="token punctuation">,</span> 0.1654 55.35%<span class="token punctuation">,</span> 0.3041 60.92%<span class="token punctuation">,</span> 0.3566 64.67%<span class="token punctuation">,</span> 0.3478 68.43%<span class="token punctuation">,</span> 0.3236 72.04%<span class="token punctuation">,</span> 0.3376 76.77%<span class="token punctuation">,</span> 0.4511 84.84%<span class="token punctuation">,</span> 0.4535 88.46%<span class="token punctuation">,</span> 0.4177 93.05%<span class="token punctuation">,</span> 0.4026 96.38%<span class="token punctuation">,</span> 0.4235 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.667<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-31</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.1775 0%<span class="token punctuation">,</span> 0.1515 5.15%<span class="token punctuation">,</span> 0.1362 9.04%<span class="token punctuation">,</span> 0.1568 12.24%<span class="token punctuation">,</span> 0.2595 17.94%<span class="token punctuation">,</span> 0.3467 22.25%<span class="token punctuation">,</span> 0.4229 26.98%<span class="token punctuation">,</span> 0.4457 29.49%<span class="token punctuation">,</span> 0.4466 29.62%<span class="token punctuation">,</span> 0.4466 29.62%<span class="token punctuation">,</span> 0.466 34.35%<span class="token punctuation">,</span> 0.4867 41.17%<span class="token punctuation">,</span> 0.5107 45.9%<span class="token punctuation">,</span> 0.5075 52.29%<span class="token punctuation">,</span> 0.5075 52.43%<span class="token punctuation">,</span> 0.5075 52.43%<span class="token punctuation">,</span> 0.4928 55.22%<span class="token punctuation">,</span> 0.4449 58.55%<span class="token punctuation">,</span> 0.3597 62.87%<span class="token punctuation">,</span> 0.1388 73.16%<span class="token punctuation">,</span> 0.0072 78.3%<span class="token punctuation">,</span> 0.0041 78.44%<span class="token punctuation">,</span> 0.0008 78.58%<span class="token punctuation">,</span> 0.9943 78.58%<span class="token punctuation">,</span> 0.9451 81.36%<span class="token punctuation">,</span> 0.9216 85.81%<span class="token punctuation">,</span> 0.9408 89.43%<span class="token punctuation">,</span> 0.9953 93.6%<span class="token punctuation">,</span> 0.9976 93.74%<span class="token punctuation">,</span> 0.0041 93.74%<span class="token punctuation">,</span> 0.0085 94.02%<span class="token punctuation">,</span> 0.0106 94.16%<span class="token punctuation">,</span> 0.0106 94.16%<span class="token punctuation">,</span> 0.0623 97.5%<span class="token punctuation">,</span> 0.1047 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4246 0%<span class="token punctuation">,</span> 0.4722 5.15%<span class="token punctuation">,</span> 0.5511 9.04%<span class="token punctuation">,</span> 0.6087 12.24%<span class="token punctuation">,</span> 0.6776 17.94%<span class="token punctuation">,</span> 0.7661 22.25%<span class="token punctuation">,</span> 0.9173 26.98%<span class="token punctuation">,</span> 0.9934 29.49%<span class="token punctuation">,</span> 0.9974 29.62%<span class="token punctuation">,</span> 0.006 29.62%<span class="token punctuation">,</span> 0.1658 34.35%<span class="token punctuation">,</span> 0.4526 41.17%<span class="token punctuation">,</span> 0.6807 45.9%<span class="token punctuation">,</span> 0.9931 52.29%<span class="token punctuation">,</span> 0.9998 52.43%<span class="token punctuation">,</span> 0.0085 52.43%<span class="token punctuation">,</span> 0.1141 55.22%<span class="token punctuation">,</span> 0.1947 58.55%<span class="token punctuation">,</span> 0.2411 62.87%<span class="token punctuation">,</span> 0.2291 73.16%<span class="token punctuation">,</span> 0.2305 78.3%<span class="token punctuation">,</span> 0.2306 78.44%<span class="token punctuation">,</span> 0.2304 78.58%<span class="token punctuation">,</span> 0.2304 78.58%<span class="token punctuation">,</span> 0.2201 81.36%<span class="token punctuation">,</span> 0.1699 85.81%<span class="token punctuation">,</span> 0.105 89.43%<span class="token punctuation">,</span> 0.0142 93.6%<span class="token punctuation">,</span> 0.0113 93.74%<span class="token punctuation">,</span> 0.0113 93.74%<span class="token punctuation">,</span> 0.0053 94.02%<span class="token punctuation">,</span> 0.0024 94.16%<span class="token punctuation">,</span> 0.9938 94.16%<span class="token punctuation">,</span> 0.9549 97.5%<span class="token punctuation">,</span> 0.9486 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-32</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.1072 0%<span class="token punctuation">,</span> 0.2103 5.01%<span class="token punctuation">,</span> 0.3128 10.29%<span class="token punctuation">,</span> 0.4529 19.19%<span class="token punctuation">,</span> 0.53 24.62%<span class="token punctuation">,</span> 0.594 28.37%<span class="token punctuation">,</span> 0.6556 31.99%<span class="token punctuation">,</span> 0.7155 36.02%<span class="token punctuation">,</span> 0.7776 40.75%<span class="token punctuation">,</span> 0.8165 46.73%<span class="token punctuation">,</span> 0.8311 53.13%<span class="token punctuation">,</span> 0.8523 58.69%<span class="token punctuation">,</span> 0.8548 58.97%<span class="token punctuation">,</span> 0.8561 59.11%<span class="token punctuation">,</span> 0.8561 59.11%<span class="token punctuation">,</span> 0.8909 61.89%<span class="token punctuation">,</span> 0.9873 67.73%<span class="token punctuation">,</span> 0.997 68.29%<span class="token punctuation">,</span> 0.9995 68.43%<span class="token punctuation">,</span> 0.0061 68.43%<span class="token punctuation">,</span> 0.0592 71.63%<span class="token punctuation">,</span> 0.1018 74.97%<span class="token punctuation">,</span> 0.1038 78.03%<span class="token punctuation">,</span> 0.0723 83.45%<span class="token punctuation">,</span> 0.0235 88.6%<span class="token punctuation">,</span> 0.0054 89.99%<span class="token punctuation">,</span> 0.0037 90.13%<span class="token punctuation">,</span> 0.9971 90.13%<span class="token punctuation">,</span> 0.9757 93.6%<span class="token punctuation">,</span> 0.9554 97.91%<span class="token punctuation">,</span> 0.9383 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.948 0%<span class="token punctuation">,</span> 0.8972 5.01%<span class="token punctuation">,</span> 0.7855 10.29%<span class="token punctuation">,</span> 0.5416 19.19%<span class="token punctuation">,</span> 0.3733 24.62%<span class="token punctuation">,</span> 0.2851 28.37%<span class="token punctuation">,</span> 0.2531 31.99%<span class="token punctuation">,</span> 0.2732 36.02%<span class="token punctuation">,</span> 0.3622 40.75%<span class="token punctuation">,</span> 0.5372 46.73%<span class="token punctuation">,</span> 0.7438 53.13%<span class="token punctuation">,</span> 0.9797 58.69%<span class="token punctuation">,</span> 0.9915 58.97%<span class="token punctuation">,</span> 0.9975 59.11%<span class="token punctuation">,</span> 0.0061 59.11%<span class="token punctuation">,</span> 0.0983 61.89%<span class="token punctuation">,</span> 0.2337 67.73%<span class="token punctuation">,</span> 0.2513 68.29%<span class="token punctuation">,</span> 0.2559 68.43%<span class="token punctuation">,</span> 0.2559 68.43%<span class="token punctuation">,</span> 0.3298 71.63%<span class="token punctuation">,</span> 0.3513 74.97%<span class="token punctuation">,</span> 0.3486 78.03%<span class="token punctuation">,</span> 0.3707 83.45%<span class="token punctuation">,</span> 0.4147 88.6%<span class="token punctuation">,</span> 0.4183 89.99%<span class="token punctuation">,</span> 0.4186 90.13%<span class="token punctuation">,</span> 0.4186 90.13%<span class="token punctuation">,</span> 0.448 93.6%<span class="token punctuation">,</span> 0.5356 97.91%<span class="token punctuation">,</span> 0.5987 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-33</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.1882 0%<span class="token punctuation">,</span> 0.1803 4.03%<span class="token punctuation">,</span> 0.1938 7.51%<span class="token punctuation">,</span> 0.2514 12.1%<span class="token punctuation">,</span> 0.328 16.97%<span class="token punctuation">,</span> 0.3989 20.58%<span class="token punctuation">,</span> 0.5595 29.9%<span class="token punctuation">,</span> 0.6362 33.52%<span class="token punctuation">,</span> 0.6882 36.72%<span class="token punctuation">,</span> 0.7249 40.19%<span class="token punctuation">,</span> 0.7389 45.62%<span class="token punctuation">,</span> 0.7454 52.16%<span class="token punctuation">,</span> 0.7628 56.88%<span class="token punctuation">,</span> 0.7692 58.14%<span class="token punctuation">,</span> 0.77 58.28%<span class="token punctuation">,</span> 0.77 58.28%<span class="token punctuation">,</span> 0.7809 61.06%<span class="token punctuation">,</span> 0.7932 64.81%<span class="token punctuation">,</span> 0.8505 69.68%<span class="token punctuation">,</span> 0.901 73.16%<span class="token punctuation">,</span> 0.9956 79.14%<span class="token punctuation">,</span> 0.9981 79.28%<span class="token punctuation">,</span> 0.0047 79.28%<span class="token punctuation">,</span> 0.0378 82.75%<span class="token punctuation">,</span> 0.0382 86.51%<span class="token punctuation">,</span> 0.0082 89.99%<span class="token punctuation">,</span> 0.0046 90.26%<span class="token punctuation">,</span> 0.0027 90.4%<span class="token punctuation">,</span> 0.9962 90.4%<span class="token punctuation">,</span> 0.9691 93.6%<span class="token punctuation">,</span> 0.9445 97.91%<span class="token punctuation">,</span> 0.9265 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5937 0%<span class="token punctuation">,</span> 0.6197 4.03%<span class="token punctuation">,</span> 0.6692 7.51%<span class="token punctuation">,</span> 0.6987 12.1%<span class="token punctuation">,</span> 0.6583 16.97%<span class="token punctuation">,</span> 0.5907 20.58%<span class="token punctuation">,</span> 0.3426 29.9%<span class="token punctuation">,</span> 0.2858 33.52%<span class="token punctuation">,</span> 0.2925 36.72%<span class="token punctuation">,</span> 0.3546 40.19%<span class="token punctuation">,</span> 0.5158 45.62%<span class="token punctuation">,</span> 0.7469 52.16%<span class="token punctuation">,</span> 0.9405 56.88%<span class="token punctuation">,</span> 0.993 58.14%<span class="token punctuation">,</span> 0.9989 58.28%<span class="token punctuation">,</span> 0.0075 58.28%<span class="token punctuation">,</span> 0.0885 61.06%<span class="token punctuation">,</span> 0.1272 64.81%<span class="token punctuation">,</span> 0.1251 69.68%<span class="token punctuation">,</span> 0.1514 73.16%<span class="token punctuation">,</span> 0.2558 79.14%<span class="token punctuation">,</span> 0.2572 79.28%<span class="token punctuation">,</span> 0.2572 79.28%<span class="token punctuation">,</span> 0.2916 82.75%<span class="token punctuation">,</span> 0.3202 86.51%<span class="token punctuation">,</span> 0.3579 89.99%<span class="token punctuation">,</span> 0.3597 90.26%<span class="token punctuation">,</span> 0.3602 90.4%<span class="token punctuation">,</span> 0.3602 90.4%<span class="token punctuation">,</span> 0.3986 93.6%<span class="token punctuation">,</span> 0.4922 97.91%<span class="token punctuation">,</span> 0.5501 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.667<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-34</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.9253 0%<span class="token punctuation">,</span> 0.9095 3.62%<span class="token punctuation">,</span> 0.9204 7.09%<span class="token punctuation">,</span> 0.9386 12.52%<span class="token punctuation">,</span> 0.9259 16.41%<span class="token punctuation">,</span> 0.8881 22.11%<span class="token punctuation">,</span> 0.8821 26.01%<span class="token punctuation">,</span> 0.9119 30.04%<span class="token punctuation">,</span> 0.9618 33.66%<span class="token punctuation">,</span> 0.9971 36.02%<span class="token punctuation">,</span> 0.999 36.16%<span class="token punctuation">,</span> 0.0056 36.16%<span class="token punctuation">,</span> 0.038 40.06%<span class="token punctuation">,</span> 0.0418 46.18%<span class="token punctuation">,</span> 0.0056 49.65%<span class="token punctuation">,</span> 0.0037 49.79%<span class="token punctuation">,</span> 0.9971 49.79%<span class="token punctuation">,</span> 0.9134 55.35%<span class="token punctuation">,</span> 0.8617 58.69%<span class="token punctuation">,</span> 0.8341 61.75%<span class="token punctuation">,</span> 0.8302 68.98%<span class="token punctuation">,</span> 0.8035 72.6%<span class="token punctuation">,</span> 0.7424 76.36%<span class="token punctuation">,</span> 0.6331 81.5%<span class="token punctuation">,</span> 0.4992 86.79%<span class="token punctuation">,</span> 0.2879 94.71%<span class="token punctuation">,</span> 0.148 99.03%<span class="token punctuation">,</span> 0.117 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.554 0%<span class="token punctuation">,</span> 0.6523 3.62%<span class="token punctuation">,</span> 0.6997 7.09%<span class="token punctuation">,</span> 0.7132 12.52%<span class="token punctuation">,</span> 0.7054 16.41%<span class="token punctuation">,</span> 0.6321 22.11%<span class="token punctuation">,</span> 0.5425 26.01%<span class="token punctuation">,</span> 0.4649 30.04%<span class="token punctuation">,</span> 0.4458 33.66%<span class="token punctuation">,</span> 0.4591 36.02%<span class="token punctuation">,</span> 0.4605 36.16%<span class="token punctuation">,</span> 0.4605 36.16%<span class="token punctuation">,</span> 0.4712 40.06%<span class="token punctuation">,</span> 0.4284 46.18%<span class="token punctuation">,</span> 0.4022 49.65%<span class="token punctuation">,</span> 0.4014 49.79%<span class="token punctuation">,</span> 0.4014 49.79%<span class="token punctuation">,</span> 0.3469 55.35%<span class="token punctuation">,</span> 0.3823 58.69%<span class="token punctuation">,</span> 0.4401 61.75%<span class="token punctuation">,</span> 0.6238 68.98%<span class="token punctuation">,</span> 0.6846 72.6%<span class="token punctuation">,</span> 0.7032 76.36%<span class="token punctuation">,</span> 0.6868 81.5%<span class="token punctuation">,</span> 0.6319 86.79%<span class="token punctuation">,</span> 0.4911 94.71%<span class="token punctuation">,</span> 0.4283 99.03%<span class="token punctuation">,</span> 0.4218 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-35</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.1126 0%<span class="token punctuation">,</span> 0.0158 3.2%<span class="token punctuation">,</span> 0.0039 3.62%<span class="token punctuation">,</span> 0 3.76%<span class="token punctuation">,</span> 0.9935 3.76%<span class="token punctuation">,</span> 0.9326 6.68%<span class="token punctuation">,</span> 0.9059 9.74%<span class="token punctuation">,</span> 0.9126 13.21%<span class="token punctuation">,</span> 0.9515 19.61%<span class="token punctuation">,</span> 0.9892 23.78%<span class="token punctuation">,</span> 0.997 24.34%<span class="token punctuation">,</span> 0.9991 24.48%<span class="token punctuation">,</span> 0.0056 24.48%<span class="token punctuation">,</span> 0.0617 27.96%<span class="token punctuation">,</span> 0.1335 32.82%<span class="token punctuation">,</span> 0.3827 45.2%<span class="token punctuation">,</span> 0.4667 49.51%<span class="token punctuation">,</span> 0.5543 53.27%<span class="token punctuation">,</span> 0.6155 56.61%<span class="token punctuation">,</span> 0.6621 60.36%<span class="token punctuation">,</span> 0.6725 62.45%<span class="token punctuation">,</span> 0.6729 62.59%<span class="token punctuation">,</span> 0.6729 62.59%<span class="token punctuation">,</span> 0.686 65.65%<span class="token punctuation">,</span> 0.7076 69.4%<span class="token punctuation">,</span> 0.7615 74.13%<span class="token punctuation">,</span> 0.8389 78.58%<span class="token punctuation">,</span> 0.9212 82.34%<span class="token punctuation">,</span> 0.9963 85.81%<span class="token punctuation">,</span> 0.9993 85.95%<span class="token punctuation">,</span> 0.0058 85.95%<span class="token punctuation">,</span> 0.0498 88.87%<span class="token punctuation">,</span> 0.0683 92.21%<span class="token punctuation">,</span> 0.0701 96.52%<span class="token punctuation">,</span> 0.0948 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.421 0%<span class="token punctuation">,</span> 0.4368 3.2%<span class="token punctuation">,</span> 0.4433 3.62%<span class="token punctuation">,</span> 0.4456 3.76%<span class="token punctuation">,</span> 0.4456 3.76%<span class="token punctuation">,</span> 0.4955 6.68%<span class="token punctuation">,</span> 0.5339 9.74%<span class="token punctuation">,</span> 0.5453 13.21%<span class="token punctuation">,</span> 0.5116 19.61%<span class="token punctuation">,</span> 0.4494 23.78%<span class="token punctuation">,</span> 0.4453 24.34%<span class="token punctuation">,</span> 0.4441 24.48%<span class="token punctuation">,</span> 0.4441 24.48%<span class="token punctuation">,</span> 0.4418 27.96%<span class="token punctuation">,</span> 0.4811 32.82%<span class="token punctuation">,</span> 0.6153 45.2%<span class="token punctuation">,</span> 0.6457 49.51%<span class="token punctuation">,</span> 0.6747 53.27%<span class="token punctuation">,</span> 0.7647 56.61%<span class="token punctuation">,</span> 0.9129 60.36%<span class="token punctuation">,</span> 0.9925 62.45%<span class="token punctuation">,</span> 0.998 62.59%<span class="token punctuation">,</span> 0.0066 62.59%<span class="token punctuation">,</span> 0.0915 65.65%<span class="token punctuation">,</span> 0.1339 69.4%<span class="token punctuation">,</span> 0.167 74.13%<span class="token punctuation">,</span> 0.2512 78.58%<span class="token punctuation">,</span> 0.296 82.34%<span class="token punctuation">,</span> 0.2829 85.81%<span class="token punctuation">,</span> 0.2821 85.95%<span class="token punctuation">,</span> 0.2821 85.95%<span class="token punctuation">,</span> 0.2932 88.87%<span class="token punctuation">,</span> 0.3104 92.21%<span class="token punctuation">,</span> 0.2781 96.52%<span class="token punctuation">,</span> 0.2208 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-36</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.8892 0%<span class="token punctuation">,</span> 0.9055 3.62%<span class="token punctuation">,</span> 0.9297 8.07%<span class="token punctuation">,</span> 0.9237 11.4%<span class="token punctuation">,</span> 0.8975 17.25%<span class="token punctuation">,</span> 0.9123 20.31%<span class="token punctuation">,</span> 0.9651 24.06%<span class="token punctuation">,</span> 0.9969 26.29%<span class="token punctuation">,</span> 0.999 26.43%<span class="token punctuation">,</span> 0.0055 26.43%<span class="token punctuation">,</span> 0.0524 30.74%<span class="token punctuation">,</span> 0.1004 41.03%<span class="token punctuation">,</span> 0.1642 47.84%<span class="token punctuation">,</span> 0.1961 52.02%<span class="token punctuation">,</span> 0.1943 56.19%<span class="token punctuation">,</span> 0.2008 59.94%<span class="token punctuation">,</span> 0.2379 63.7%<span class="token punctuation">,</span> 0.3299 68.43%<span class="token punctuation">,</span> 0.3818 71.21%<span class="token punctuation">,</span> 0.4131 74.83%<span class="token punctuation">,</span> 0.4428 83.03%<span class="token punctuation">,</span> 0.4898 87.2%<span class="token punctuation">,</span> 0.5511 91.24%<span class="token punctuation">,</span> 0.6537 97.08%<span class="token punctuation">,</span> 0.7022 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.0628 0%<span class="token punctuation">,</span> 0.0786 3.62%<span class="token punctuation">,</span> 0.1486 8.07%<span class="token punctuation">,</span> 0.2074 11.4%<span class="token punctuation">,</span> 0.3789 17.25%<span class="token punctuation">,</span> 0.4614 20.31%<span class="token punctuation">,</span> 0.5296 24.06%<span class="token punctuation">,</span> 0.549 26.29%<span class="token punctuation">,</span> 0.5498 26.43%<span class="token punctuation">,</span> 0.5498 26.43%<span class="token punctuation">,</span> 0.5873 30.74%<span class="token punctuation">,</span> 0.7434 41.03%<span class="token punctuation">,</span> 0.8385 47.84%<span class="token punctuation">,</span> 0.836 52.02%<span class="token punctuation">,</span> 0.7893 56.19%<span class="token punctuation">,</span> 0.7275 59.94%<span class="token punctuation">,</span> 0.6837 63.7%<span class="token punctuation">,</span> 0.6642 68.43%<span class="token punctuation">,</span> 0.6139 71.21%<span class="token punctuation">,</span> 0.5003 74.83%<span class="token punctuation">,</span> 0.2322 83.03%<span class="token punctuation">,</span> 0.1183 87.2%<span class="token punctuation">,</span> 0.0765 91.24%<span class="token punctuation">,</span> 0.0905 97.08%<span class="token punctuation">,</span> 0.1046 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.667<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-37</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7044 0%<span class="token punctuation">,</span> 0.7539 4.17%<span class="token punctuation">,</span> 0.7569 7.79%<span class="token punctuation">,</span> 0.7295 11.68%<span class="token punctuation">,</span> 0.6392 17.94%<span class="token punctuation">,</span> 0.5812 21.7%<span class="token punctuation">,</span> 0.5438 25.87%<span class="token punctuation">,</span> 0.5543 30.18%<span class="token punctuation">,</span> 0.6178 35.47%<span class="token punctuation">,</span> 0.7229 40.61%<span class="token punctuation">,</span> 0.9205 49.1%<span class="token punctuation">,</span> 0.9955 52.57%<span class="token punctuation">,</span> 0.9984 52.71%<span class="token punctuation">,</span> 0.005 52.71%<span class="token punctuation">,</span> 0.0526 55.63%<span class="token punctuation">,</span> 0.0774 59.53%<span class="token punctuation">,</span> 0.0681 63.84%<span class="token punctuation">,</span> 0.0684 68.98%<span class="token punctuation">,</span> 0.1094 73.16%<span class="token punctuation">,</span> 0.1665 76.36%<span class="token punctuation">,</span> 0.2823 81.22%<span class="token punctuation">,</span> 0.312 83.87%<span class="token punctuation">,</span> 0.316 86.09%<span class="token punctuation">,</span> 0.3158 86.23%<span class="token punctuation">,</span> 0.3158 86.23%<span class="token punctuation">,</span> 0.3227 89.57%<span class="token punctuation">,</span> 0.3562 92.91%<span class="token punctuation">,</span> 0.4408 98.33%<span class="token punctuation">,</span> 0.4513 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.1056 0%<span class="token punctuation">,</span> 0.1496 4.17%<span class="token punctuation">,</span> 0.2057 7.79%<span class="token punctuation">,</span> 0.2265 11.68%<span class="token punctuation">,</span> 0.1903 17.94%<span class="token punctuation">,</span> 0.1832 21.7%<span class="token punctuation">,</span> 0.2474 25.87%<span class="token punctuation">,</span> 0.3413 30.18%<span class="token punctuation">,</span> 0.4472 35.47%<span class="token punctuation">,</span> 0.5496 40.61%<span class="token punctuation">,</span> 0.7103 49.1%<span class="token punctuation">,</span> 0.803 52.57%<span class="token punctuation">,</span> 0.807 52.71%<span class="token punctuation">,</span> 0.807 52.71%<span class="token punctuation">,</span> 0.8676 55.63%<span class="token punctuation">,</span> 0.8978 59.53%<span class="token punctuation">,</span> 0.8946 63.84%<span class="token punctuation">,</span> 0.8293 68.98%<span class="token punctuation">,</span> 0.8151 73.16%<span class="token punctuation">,</span> 0.8363 76.36%<span class="token punctuation">,</span> 0.8823 81.22%<span class="token punctuation">,</span> 0.9412 83.87%<span class="token punctuation">,</span> 0.9934 86.09%<span class="token punctuation">,</span> 0.9965 86.23%<span class="token punctuation">,</span> 0.0051 86.23%<span class="token punctuation">,</span> 0.0483 89.57%<span class="token punctuation">,</span> 0.0661 92.91%<span class="token punctuation">,</span> 0.1158 98.33%<span class="token punctuation">,</span> 0.1414 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-38</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4516 0%<span class="token punctuation">,</span> 0.44 4.03%<span class="token punctuation">,</span> 0.4179 7.09%<span class="token punctuation">,</span> 0.3643 11.68%<span class="token punctuation">,</span> 0.2742 16.69%<span class="token punctuation">,</span> 0.2186 19.75%<span class="token punctuation">,</span> 0.1803 23.09%<span class="token punctuation">,</span> 0.1669 26.43%<span class="token punctuation">,</span> 0.1898 30.74%<span class="token punctuation">,</span> 0.2788 37.83%<span class="token punctuation">,</span> 0.2943 42.42%<span class="token punctuation">,</span> 0.2981 49.24%<span class="token punctuation">,</span> 0.2981 49.37%<span class="token punctuation">,</span> 0.2981 49.37%<span class="token punctuation">,</span> 0.3179 54.38%<span class="token punctuation">,</span> 0.358 58.14%<span class="token punctuation">,</span> 0.388 61.47%<span class="token punctuation">,</span> 0.3845 65.23%<span class="token punctuation">,</span> 0.3458 69.82%<span class="token punctuation">,</span> 0.2718 74.97%<span class="token punctuation">,</span> 0.182 82.2%<span class="token punctuation">,</span> 0.1302 86.37%<span class="token punctuation">,</span> 0.0998 89.57%<span class="token punctuation">,</span> 0.1039 93.18%<span class="token punctuation">,</span> 0.135 96.38%<span class="token punctuation">,</span> 0.153 99.17%<span class="token punctuation">,</span> 0.1505 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.1435 0%<span class="token punctuation">,</span> 0.2023 4.03%<span class="token punctuation">,</span> 0.2021 7.09%<span class="token punctuation">,</span> 0.1421 11.68%<span class="token punctuation">,</span> 0.0852 16.69%<span class="token punctuation">,</span> 0.0891 19.75%<span class="token punctuation">,</span> 0.1519 23.09%<span class="token punctuation">,</span> 0.2474 26.43%<span class="token punctuation">,</span> 0.3869 30.74%<span class="token punctuation">,</span> 0.6188 37.83%<span class="token punctuation">,</span> 0.7928 42.42%<span class="token punctuation">,</span> 0.992 49.24%<span class="token punctuation">,</span> 0.9961 49.37%<span class="token punctuation">,</span> 0.0047 49.37%<span class="token punctuation">,</span> 0.1314 54.38%<span class="token punctuation">,</span> 0.1903 58.14%<span class="token punctuation">,</span> 0.1979 61.47%<span class="token punctuation">,</span> 0.1838 65.23%<span class="token punctuation">,</span> 0.1999 69.82%<span class="token punctuation">,</span> 0.2748 74.97%<span class="token punctuation">,</span> 0.417 82.2%<span class="token punctuation">,</span> 0.4902 86.37%<span class="token punctuation">,</span> 0.4774 89.57%<span class="token punctuation">,</span> 0.4399 93.18%<span class="token punctuation">,</span> 0.4123 96.38%<span class="token punctuation">,</span> 0.4294 99.17%<span class="token punctuation">,</span> 0.4401 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-39</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.8487 0%<span class="token punctuation">,</span> 0.8428 3.48%<span class="token punctuation">,</span> 0.8012 7.93%<span class="token punctuation">,</span> 0.7226 13.35%<span class="token punctuation">,</span> 0.6404 17.8%<span class="token punctuation">,</span> 0.5301 23.09%<span class="token punctuation">,</span> 0.4548 26.98%<span class="token punctuation">,</span> 0.4055 30.18%<span class="token punctuation">,</span> 0.3656 33.52%<span class="token punctuation">,</span> 0.3459 37.55%<span class="token punctuation">,</span> 0.3706 41.59%<span class="token punctuation">,</span> 0.4188 46.18%<span class="token punctuation">,</span> 0.4864 51.74%<span class="token punctuation">,</span> 0.5224 55.49%<span class="token punctuation">,</span> 0.5233 55.63%<span class="token punctuation">,</span> 0.5233 55.63%<span class="token punctuation">,</span> 0.5362 60.5%<span class="token punctuation">,</span> 0.481 67.32%<span class="token punctuation">,</span> 0.4454 70.65%<span class="token punctuation">,</span> 0.3996 74.27%<span class="token punctuation">,</span> 0.3436 78.16%<span class="token punctuation">,</span> 0.2916 82.34%<span class="token punctuation">,</span> 0.2201 89.99%<span class="token punctuation">,</span> 0.193 94.99%<span class="token punctuation">,</span> 0.1921 95.13%<span class="token punctuation">,</span> 0.1921 95.13%<span class="token punctuation">,</span> 0.1944 98.75%<span class="token punctuation">,</span> 0.2035 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.1461 0%<span class="token punctuation">,</span> 0.1634 3.48%<span class="token punctuation">,</span> 0.21 7.93%<span class="token punctuation">,</span> 0.2188 13.35%<span class="token punctuation">,</span> 0.1507 17.8%<span class="token punctuation">,</span> 0.0628 23.09%<span class="token punctuation">,</span> 0.0653 26.98%<span class="token punctuation">,</span> 0.129 30.18%<span class="token punctuation">,</span> 0.2473 33.52%<span class="token punctuation">,</span> 0.4073 37.55%<span class="token punctuation">,</span> 0.5517 41.59%<span class="token punctuation">,</span> 0.6803 46.18%<span class="token punctuation">,</span> 0.8518 51.74%<span class="token punctuation">,</span> 0.9924 55.49%<span class="token punctuation">,</span> 0.9982 55.63%<span class="token punctuation">,</span> 0.0068 55.63%<span class="token punctuation">,</span> 0.1954 60.5%<span class="token punctuation">,</span> 0.4353 67.32%<span class="token punctuation">,</span> 0.4921 70.65%<span class="token punctuation">,</span> 0.5056 74.27%<span class="token punctuation">,</span> 0.4697 78.16%<span class="token punctuation">,</span> 0.3904 82.34%<span class="token punctuation">,</span> 0.1712 89.99%<span class="token punctuation">,</span> 0.0075 94.99%<span class="token punctuation">,</span> 0.0032 95.13%<span class="token punctuation">,</span> 0.9946 95.13%<span class="token punctuation">,</span> 0.9024 98.75%<span class="token punctuation">,</span> 0.8759 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.667<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-40</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.2047 0%<span class="token punctuation">,</span> 0.2521 3.62%<span class="token punctuation">,</span> 0.4538 14.88%<span class="token punctuation">,</span> 0.6753 26.01%<span class="token punctuation">,</span> 0.7697 30.74%<span class="token punctuation">,</span> 0.8675 35.33%<span class="token punctuation">,</span> 0.9314 39.92%<span class="token punctuation">,</span> 0.961 43.67%<span class="token punctuation">,</span> 0.9653 47.98%<span class="token punctuation">,</span> 0.9272 54.52%<span class="token punctuation">,</span> 0.8817 60.92%<span class="token punctuation">,</span> 0.8813 61.06%<span class="token punctuation">,</span> 0.8808 61.2%<span class="token punctuation">,</span> 0.8808 61.2%<span class="token punctuation">,</span> 0.8938 64.67%<span class="token punctuation">,</span> 0.9511 68.57%<span class="token punctuation">,</span> 0.9957 70.79%<span class="token punctuation">,</span> 0.9983 70.93%<span class="token punctuation">,</span> 0.0049 70.93%<span class="token punctuation">,</span> 0.0535 73.99%<span class="token punctuation">,</span> 0.0886 77.05%<span class="token punctuation">,</span> 0.0933 80.95%<span class="token punctuation">,</span> 0.0677 84.98%<span class="token punctuation">,</span> 0.0042 89.57%<span class="token punctuation">,</span> 0.0016 89.71%<span class="token punctuation">,</span> 0.995 89.71%<span class="token punctuation">,</span> 0.9605 92.63%<span class="token punctuation">,</span> 0.9268 97.91%<span class="token punctuation">,</span> 0.907 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.8727 0%<span class="token punctuation">,</span> 0.7867 3.62%<span class="token punctuation">,</span> 0.5516 14.88%<span class="token punctuation">,</span> 0.3151 26.01%<span class="token punctuation">,</span> 0.2813 30.74%<span class="token punctuation">,</span> 0.3013 35.33%<span class="token punctuation">,</span> 0.3817 39.92%<span class="token punctuation">,</span> 0.4609 43.67%<span class="token punctuation">,</span> 0.5682 47.98%<span class="token punctuation">,</span> 0.7663 54.52%<span class="token punctuation">,</span> 0.9898 60.92%<span class="token punctuation">,</span> 0.9946 61.06%<span class="token punctuation">,</span> 0.9995 61.2%<span class="token punctuation">,</span> 0.0081 61.2%<span class="token punctuation">,</span> 0.1238 64.67%<span class="token punctuation">,</span> 0.2446 68.57%<span class="token punctuation">,</span> 0.3078 70.79%<span class="token punctuation">,</span> 0.3113 70.93%<span class="token punctuation">,</span> 0.3113 70.93%<span class="token punctuation">,</span> 0.3641 73.99%<span class="token punctuation">,</span> 0.3672 77.05%<span class="token punctuation">,</span> 0.3439 80.95%<span class="token punctuation">,</span> 0.3583 84.98%<span class="token punctuation">,</span> 0.382 89.57%<span class="token punctuation">,</span> 0.3823 89.71%<span class="token punctuation">,</span> 0.3823 89.71%<span class="token punctuation">,</span> 0.4071 92.63%<span class="token punctuation">,</span> 0.5254 97.91%<span class="token punctuation">,</span> 0.5828 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-41</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.9056 0%<span class="token punctuation">,</span> 0.8905 3.62%<span class="token punctuation">,</span> 0.8997 6.54%<span class="token punctuation">,</span> 0.9126 10.43%<span class="token punctuation">,</span> 0.8936 16.55%<span class="token punctuation">,</span> 0.8695 21.42%<span class="token punctuation">,</span> 0.8739 25.45%<span class="token punctuation">,</span> 0.9024 28.65%<span class="token punctuation">,</span> 0.9828 33.66%<span class="token punctuation">,</span> 0.9956 34.35%<span class="token punctuation">,</span> 0.9983 34.49%<span class="token punctuation">,</span> 0.0048 34.49%<span class="token punctuation">,</span> 0.07 38.25%<span class="token punctuation">,</span> 0.0976 41.45%<span class="token punctuation">,</span> 0.094 46.45%<span class="token punctuation">,</span> 0.0613 50.9%<span class="token punctuation">,</span> 0.055 55.08%<span class="token punctuation">,</span> 0.089 60.22%<span class="token punctuation">,</span> 0.1395 65.23%<span class="token punctuation">,</span> 0.1541 68.98%<span class="token punctuation">,</span> 0.1241 72.88%<span class="token punctuation">,</span> 0.116 73.44%<span class="token punctuation">,</span> 0.1139 73.57%<span class="token punctuation">,</span> 0.1139 73.57%<span class="token punctuation">,</span> 0.095 76.5%<span class="token punctuation">,</span> 0.1096 80.11%<span class="token punctuation">,</span> 0.1552 83.31%<span class="token punctuation">,</span> 0.1904 86.51%<span class="token punctuation">,</span> 0.196 87.9%<span class="token punctuation">,</span> 0.1963 88.04%<span class="token punctuation">,</span> 0.1963 88.04%<span class="token punctuation">,</span> 0.1975 90.96%<span class="token punctuation">,</span> 0.1977 94.85%<span class="token punctuation">,</span> 0.1897 99.44%<span class="token punctuation">,</span> 0.1893 99.58%<span class="token punctuation">,</span> 0.1893 99.58%<span class="token punctuation">,</span> 0.1883 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5867 0%<span class="token punctuation">,</span> 0.6875 3.62%<span class="token punctuation">,</span> 0.7279 6.54%<span class="token punctuation">,</span> 0.7321 10.43%<span class="token punctuation">,</span> 0.6831 16.55%<span class="token punctuation">,</span> 0.5974 21.42%<span class="token punctuation">,</span> 0.5278 25.45%<span class="token punctuation">,</span> 0.496 28.65%<span class="token punctuation">,</span> 0.4889 33.66%<span class="token punctuation">,</span> 0.4924 34.35%<span class="token punctuation">,</span> 0.4934 34.49%<span class="token punctuation">,</span> 0.4934 34.49%<span class="token punctuation">,</span> 0.4956 38.25%<span class="token punctuation">,</span> 0.4615 41.45%<span class="token punctuation">,</span> 0.3845 46.45%<span class="token punctuation">,</span> 0.2872 50.9%<span class="token punctuation">,</span> 0.2086 55.08%<span class="token punctuation">,</span> 0.155 60.22%<span class="token punctuation">,</span> 0.1266 65.23%<span class="token punctuation">,</span> 0.0709 68.98%<span class="token punctuation">,</span> 0.0136 72.88%<span class="token punctuation">,</span> 0.0041 73.44%<span class="token punctuation">,</span> 0.0015 73.57%<span class="token punctuation">,</span> 0.9929 73.57%<span class="token punctuation">,</span> 0.9434 76.5%<span class="token punctuation">,</span> 0.8988 80.11%<span class="token punctuation">,</span> 0.9002 83.31%<span class="token punctuation">,</span> 0.9564 86.51%<span class="token punctuation">,</span> 0.9922 87.9%<span class="token punctuation">,</span> 0.9961 88.04%<span class="token punctuation">,</span> 0.0047 88.04%<span class="token punctuation">,</span> 0.0584 90.96%<span class="token punctuation">,</span> 0.0526 94.85%<span class="token punctuation">,</span> 0.0037 99.44%<span class="token punctuation">,</span> 0.0023 99.58%<span class="token punctuation">,</span> 0.9936 99.58%<span class="token punctuation">,</span> 0.9887 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-42</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7898 0%<span class="token punctuation">,</span> 0.769 3.76%<span class="token punctuation">,</span> 0.6907 8.48%<span class="token punctuation">,</span> 0.5644 15.3%<span class="token punctuation">,</span> 0.5224 19.19%<span class="token punctuation">,</span> 0.5137 24.06%<span class="token punctuation">,</span> 0.5406 28.79%<span class="token punctuation">,</span> 0.5975 33.1%<span class="token punctuation">,</span> 0.6668 36.58%<span class="token punctuation">,</span> 0.7825 41.17%<span class="token punctuation">,</span> 0.9041 46.73%<span class="token punctuation">,</span> 0.9493 50.21%<span class="token punctuation">,</span> 0.9693 54.1%<span class="token punctuation">,</span> 0.9543 57.86%<span class="token punctuation">,</span> 0.906 61.75%<span class="token punctuation">,</span> 0.8073 66.34%<span class="token punctuation">,</span> 0.711 70.24%<span class="token punctuation">,</span> 0.639 73.85%<span class="token punctuation">,</span> 0.5461 79%<span class="token punctuation">,</span> 0.4169 84.42%<span class="token punctuation">,</span> 0.2179 92.07%<span class="token punctuation">,</span> 0.1512 95.27%<span class="token punctuation">,</span> 0.123 98.19%<span class="token punctuation">,</span> 0.1221 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.3462 0%<span class="token punctuation">,</span> 0.353 3.76%<span class="token punctuation">,</span> 0.3686 8.48%<span class="token punctuation">,</span> 0.4174 15.3%<span class="token punctuation">,</span> 0.4703 19.19%<span class="token punctuation">,</span> 0.4694 24.06%<span class="token punctuation">,</span> 0.438 28.79%<span class="token punctuation">,</span> 0.4069 33.1%<span class="token punctuation">,</span> 0.4123 36.58%<span class="token punctuation">,</span> 0.4675 41.17%<span class="token punctuation">,</span> 0.5551 46.73%<span class="token punctuation">,</span> 0.6084 50.21%<span class="token punctuation">,</span> 0.6649 54.1%<span class="token punctuation">,</span> 0.6944 57.86%<span class="token punctuation">,</span> 0.6791 61.75%<span class="token punctuation">,</span> 0.6264 66.34%<span class="token punctuation">,</span> 0.6149 70.24%<span class="token punctuation">,</span> 0.6554 73.85%<span class="token punctuation">,</span> 0.7536 79%<span class="token punctuation">,</span> 0.7949 84.42%<span class="token punctuation">,</span> 0.8387 92.07%<span class="token punctuation">,</span> 0.8874 95.27%<span class="token punctuation">,</span> 0.9408 98.19%<span class="token punctuation">,</span> 0.9637 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.667<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-43</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.1223 0%<span class="token punctuation">,</span> 0.1533 4.03%<span class="token punctuation">,</span> 0.2177 8.07%<span class="token punctuation">,</span> 0.365 14.33%<span class="token punctuation">,</span> 0.5635 21.56%<span class="token punctuation">,</span> 0.7193 26.7%<span class="token punctuation">,</span> 0.8399 30.74%<span class="token punctuation">,</span> 0.9348 34.63%<span class="token punctuation">,</span> 0.9956 37.55%<span class="token punctuation">,</span> 0.9984 37.69%<span class="token punctuation">,</span> 0.0049 37.69%<span class="token punctuation">,</span> 0.0959 44.09%<span class="token punctuation">,</span> 0.123 47.71%<span class="token punctuation">,</span> 0.1292 53.96%<span class="token punctuation">,</span> 0.165 57.86%<span class="token punctuation">,</span> 0.2428 63.84%<span class="token punctuation">,</span> 0.2939 67.73%<span class="token punctuation">,</span> 0.3031 70.51%<span class="token punctuation">,</span> 0.2877 74.27%<span class="token punctuation">,</span> 0.2444 79.97%<span class="token punctuation">,</span> 0.1901 84.84%<span class="token punctuation">,</span> 0.1095 90.54%<span class="token punctuation">,</span> 0.0838 94.16%<span class="token punctuation">,</span> 0.1038 98.19%<span class="token punctuation">,</span> 0.122 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.9649 0%<span class="token punctuation">,</span> 0.9775 4.03%<span class="token punctuation">,</span> 0.9633 8.07%<span class="token punctuation">,</span> 0.9306 14.33%<span class="token punctuation">,</span> 0.9368 21.56%<span class="token punctuation">,</span> 0.9197 26.7%<span class="token punctuation">,</span> 0.8699 30.74%<span class="token punctuation">,</span> 0.7745 34.63%<span class="token punctuation">,</span> 0.6999 37.55%<span class="token punctuation">,</span> 0.6964 37.69%<span class="token punctuation">,</span> 0.6964 37.69%<span class="token punctuation">,</span> 0.5561 44.09%<span class="token punctuation">,</span> 0.4923 47.71%<span class="token punctuation">,</span> 0.3703 53.96%<span class="token punctuation">,</span> 0.315 57.86%<span class="token punctuation">,</span> 0.3246 63.84%<span class="token punctuation">,</span> 0.2967 67.73%<span class="token punctuation">,</span> 0.2666 70.51%<span class="token punctuation">,</span> 0.2641 74.27%<span class="token punctuation">,</span> 0.3077 79.97%<span class="token punctuation">,</span> 0.3633 84.84%<span class="token punctuation">,</span> 0.369 90.54%<span class="token punctuation">,</span> 0.3307 94.16%<span class="token punctuation">,</span> 0.2747 98.19%<span class="token punctuation">,</span> 0.2611 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-44</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.1235 0%<span class="token punctuation">,</span> 0.1461 3.2%<span class="token punctuation">,</span> 0.1409 6.4%<span class="token punctuation">,</span> 0.1052 10.99%<span class="token punctuation">,</span> 0.0884 15.16%<span class="token punctuation">,</span> 0.0608 19.33%<span class="token punctuation">,</span> 0.0052 23.23%<span class="token punctuation">,</span> 0.0034 23.37%<span class="token punctuation">,</span> 0.9969 23.37%<span class="token punctuation">,</span> 0.9407 27.96%<span class="token punctuation">,</span> 0.8935 32.27%<span class="token punctuation">,</span> 0.8921 36.16%<span class="token punctuation">,</span> 0.9257 41.45%<span class="token punctuation">,</span> 0.9752 46.18%<span class="token punctuation">,</span> 0.9962 48.54%<span class="token punctuation">,</span> 0.9973 48.68%<span class="token punctuation">,</span> 0.0038 48.68%<span class="token punctuation">,</span> 0.0164 53.41%<span class="token punctuation">,</span> 0.0043 56.47%<span class="token punctuation">,</span> 0.0037 56.61%<span class="token punctuation">,</span> 0.9972 56.61%<span class="token punctuation">,</span> 0.9613 60.5%<span class="token punctuation">,</span> 0.9038 64.53%<span class="token punctuation">,</span> 0.8435 68.98%<span class="token punctuation">,</span> 0.7542 74.97%<span class="token punctuation">,</span> 0.6503 79.97%<span class="token punctuation">,</span> 0.5041 86.37%<span class="token punctuation">,</span> 0.3305 94.3%<span class="token punctuation">,</span> 0.2026 99.3%<span class="token punctuation">,</span> 0.1832 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.2605 0%<span class="token punctuation">,</span> 0.2743 3.2%<span class="token punctuation">,</span> 0.3145 6.4%<span class="token punctuation">,</span> 0.394 10.99%<span class="token punctuation">,</span> 0.5203 15.16%<span class="token punctuation">,</span> 0.6741 19.33%<span class="token punctuation">,</span> 0.7675 23.23%<span class="token punctuation">,</span> 0.77 23.37%<span class="token punctuation">,</span> 0.77 23.37%<span class="token punctuation">,</span> 0.8182 27.96%<span class="token punctuation">,</span> 0.8123 32.27%<span class="token punctuation">,</span> 0.7776 36.16%<span class="token punctuation">,</span> 0.6912 41.45%<span class="token punctuation">,</span> 0.633 46.18%<span class="token punctuation">,</span> 0.6249 48.54%<span class="token punctuation">,</span> 0.6253 48.68%<span class="token punctuation">,</span> 0.6253 48.68%<span class="token punctuation">,</span> 0.6148 53.41%<span class="token punctuation">,</span> 0.5891 56.47%<span class="token punctuation">,</span> 0.5878 56.61%<span class="token punctuation">,</span> 0.5878 56.61%<span class="token punctuation">,</span> 0.5313 60.5%<span class="token punctuation">,</span> 0.4838 64.53%<span class="token punctuation">,</span> 0.5145 68.98%<span class="token punctuation">,</span> 0.6334 74.97%<span class="token punctuation">,</span> 0.6804 79.97%<span class="token punctuation">,</span> 0.649 86.37%<span class="token punctuation">,</span> 0.5149 94.3%<span class="token punctuation">,</span> 0.4567 99.3%<span class="token punctuation">,</span> 0.4526 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-45</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.474 0%<span class="token punctuation">,</span> 0.4658 3.06%<span class="token punctuation">,</span> 0.417 7.37%<span class="token punctuation">,</span> 0.3731 10.57%<span class="token punctuation">,</span> 0.2798 14.88%<span class="token punctuation">,</span> 0.1981 18.08%<span class="token punctuation">,</span> 0.1476 21%<span class="token punctuation">,</span> 0.1218 24.62%<span class="token punctuation">,</span> 0.1394 28.51%<span class="token punctuation">,</span> 0.1954 32.68%<span class="token punctuation">,</span> 0.3509 39.92%<span class="token punctuation">,</span> 0.4167 43.81%<span class="token punctuation">,</span> 0.4621 48.4%<span class="token punctuation">,</span> 0.4759 52.99%<span class="token punctuation">,</span> 0.476 53.13%<span class="token punctuation">,</span> 0.476 53.13%<span class="token punctuation">,</span> 0.4668 55.91%<span class="token punctuation">,</span> 0.4271 59.81%<span class="token punctuation">,</span> 0.3237 66.62%<span class="token punctuation">,</span> 0.1887 74.83%<span class="token punctuation">,</span> 0.0822 82.2%<span class="token punctuation">,</span> 0.0068 85.95%<span class="token punctuation">,</span> 0.0034 86.09%<span class="token punctuation">,</span> 0.9969 86.09%<span class="token punctuation">,</span> 0.9326 89.57%<span class="token punctuation">,</span> 0.8754 93.74%<span class="token punctuation">,</span> 0.7731 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.0145 0%<span class="token punctuation">,</span> 0.0391 3.06%<span class="token punctuation">,</span> 0.0806 7.37%<span class="token punctuation">,</span> 0.0684 10.57%<span class="token punctuation">,</span> 0.0181 14.88%<span class="token punctuation">,</span> 0.0162 18.08%<span class="token punctuation">,</span> 0.069 21%<span class="token punctuation">,</span> 0.1661 24.62%<span class="token punctuation">,</span> 0.2772 28.51%<span class="token punctuation">,</span> 0.3901 32.68%<span class="token punctuation">,</span> 0.5555 39.92%<span class="token punctuation">,</span> 0.6666 43.81%<span class="token punctuation">,</span> 0.8248 48.4%<span class="token punctuation">,</span> 0.9918 52.99%<span class="token punctuation">,</span> 0.9972 53.13%<span class="token punctuation">,</span> 0.0058 53.13%<span class="token punctuation">,</span> 0.0842 55.91%<span class="token punctuation">,</span> 0.1325 59.81%<span class="token punctuation">,</span> 0.1859 66.62%<span class="token punctuation">,</span> 0.2744 74.83%<span class="token punctuation">,</span> 0.4186 82.2%<span class="token punctuation">,</span> 0.4548 85.95%<span class="token punctuation">,</span> 0.4554 86.09%<span class="token punctuation">,</span> 0.4554 86.09%<span class="token punctuation">,</span> 0.4835 89.57%<span class="token punctuation">,</span> 0.5508 93.74%<span class="token punctuation">,</span> 0.6701 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.667<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-46</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7712 0%<span class="token punctuation">,</span> 0.7268 4.59%<span class="token punctuation">,</span> 0.7315 8.48%<span class="token punctuation">,</span> 0.749 13.63%<span class="token punctuation">,</span> 0.7494 19.33%<span class="token punctuation">,</span> 0.7503 23.37%<span class="token punctuation">,</span> 0.7804 30.18%<span class="token punctuation">,</span> 0.8441 37.13%<span class="token punctuation">,</span> 0.8954 40.75%<span class="token punctuation">,</span> 0.9412 44.92%<span class="token punctuation">,</span> 0.9487 47.98%<span class="token punctuation">,</span> 0.9166 51.74%<span class="token punctuation">,</span> 0.8317 56.75%<span class="token punctuation">,</span> 0.76 60.22%<span class="token punctuation">,</span> 0.686 64.53%<span class="token punctuation">,</span> 0.5871 71.07%<span class="token punctuation">,</span> 0.5149 74.27%<span class="token punctuation">,</span> 0.5117 74.41%<span class="token punctuation">,</span> 0.5117 74.41%<span class="token punctuation">,</span> 0.4658 77.19%<span class="token punctuation">,</span> 0.4524 80.39%<span class="token punctuation">,</span> 0.4752 83.73%<span class="token punctuation">,</span> 0.5407 88.87%<span class="token punctuation">,</span> 0.602 93.6%<span class="token punctuation">,</span> 0.6765 98.61%<span class="token punctuation">,</span> 0.6852 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.6735 0%<span class="token punctuation">,</span> 0.7799 4.59%<span class="token punctuation">,</span> 0.8332 8.48%<span class="token punctuation">,</span> 0.8273 13.63%<span class="token punctuation">,</span> 0.7787 19.33%<span class="token punctuation">,</span> 0.7155 23.37%<span class="token punctuation">,</span> 0.5667 30.18%<span class="token punctuation">,</span> 0.4174 37.13%<span class="token punctuation">,</span> 0.3943 40.75%<span class="token punctuation">,</span> 0.427 44.92%<span class="token punctuation">,</span> 0.4689 47.98%<span class="token punctuation">,</span> 0.4945 51.74%<span class="token punctuation">,</span> 0.5138 56.75%<span class="token punctuation">,</span> 0.5659 60.22%<span class="token punctuation">,</span> 0.6851 64.53%<span class="token punctuation">,</span> 0.9116 71.07%<span class="token punctuation">,</span> 0.9946 74.27%<span class="token punctuation">,</span> 0.998 74.41%<span class="token punctuation">,</span> 0.0066 74.41%<span class="token punctuation">,</span> 0.0702 77.19%<span class="token punctuation">,</span> 0.1167 80.39%<span class="token punctuation">,</span> 0.1311 83.73%<span class="token punctuation">,</span> 0.1404 88.87%<span class="token punctuation">,</span> 0.2135 93.6%<span class="token punctuation">,</span> 0.3113 98.61%<span class="token punctuation">,</span> 0.3429 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-47</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.6857 0%<span class="token punctuation">,</span> 0.6802 3.48%<span class="token punctuation">,</span> 0.6456 6.95%<span class="token punctuation">,</span> 0.5732 11.68%<span class="token punctuation">,</span> 0.5132 15.44%<span class="token punctuation">,</span> 0.49 19.05%<span class="token punctuation">,</span> 0.4958 22.67%<span class="token punctuation">,</span> 0.5257 26.29%<span class="token punctuation">,</span> 0.5844 30.32%<span class="token punctuation">,</span> 0.7193 36.72%<span class="token punctuation">,</span> 0.8054 40.47%<span class="token punctuation">,</span> 0.8716 43.95%<span class="token punctuation">,</span> 0.9279 48.96%<span class="token punctuation">,</span> 0.9457 53.69%<span class="token punctuation">,</span> 0.9298 57.86%<span class="token punctuation">,</span> 0.876 62.17%<span class="token punctuation">,</span> 0.8542 63.56%<span class="token punctuation">,</span> 0.8522 63.7%<span class="token punctuation">,</span> 0.8522 63.7%<span class="token punctuation">,</span> 0.8373 66.48%<span class="token punctuation">,</span> 0.8581 70.79%<span class="token punctuation">,</span> 0.9193 75.94%<span class="token punctuation">,</span> 0.9733 78.86%<span class="token punctuation">,</span> 0.9971 79.97%<span class="token punctuation">,</span> 1 80.11%<span class="token punctuation">,</span> 0.0065 80.11%<span class="token punctuation">,</span> 0.0483 83.03%<span class="token punctuation">,</span> 0.067 87.62%<span class="token punctuation">,</span> 0.0557 91.52%<span class="token punctuation">,</span> 0.0318 95.27%<span class="token punctuation">,</span> 0.0294 98.47%<span class="token punctuation">,</span> 0.0371 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.3456 0%<span class="token punctuation">,</span> 0.3843 3.48%<span class="token punctuation">,</span> 0.3768 6.95%<span class="token punctuation">,</span> 0.3357 11.68%<span class="token punctuation">,</span> 0.3204 15.44%<span class="token punctuation">,</span> 0.3256 19.05%<span class="token punctuation">,</span> 0.3492 22.67%<span class="token punctuation">,</span> 0.3757 26.29%<span class="token punctuation">,</span> 0.3699 30.32%<span class="token punctuation">,</span> 0.3627 36.72%<span class="token punctuation">,</span> 0.3962 40.47%<span class="token punctuation">,</span> 0.4695 43.95%<span class="token punctuation">,</span> 0.6058 48.96%<span class="token punctuation">,</span> 0.7258 53.69%<span class="token punctuation">,</span> 0.824 57.86%<span class="token punctuation">,</span> 0.9513 62.17%<span class="token punctuation">,</span> 0.9949 63.56%<span class="token punctuation">,</span> 0.9995 63.7%<span class="token punctuation">,</span> 0.0081 63.7%<span class="token punctuation">,</span> 0.0936 66.48%<span class="token punctuation">,</span> 0.2029 70.79%<span class="token punctuation">,</span> 0.3188 75.94%<span class="token punctuation">,</span> 0.3476 78.86%<span class="token punctuation">,</span> 0.3491 79.97%<span class="token punctuation">,</span> 0.3489 80.11%<span class="token punctuation">,</span> 0.3489 80.11%<span class="token punctuation">,</span> 0.3443 83.03%<span class="token punctuation">,</span> 0.3598 87.62%<span class="token punctuation">,</span> 0.3632 91.52%<span class="token punctuation">,</span> 0.3221 95.27%<span class="token punctuation">,</span> 0.2605 98.47%<span class="token punctuation">,</span> 0.2265 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-48</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5158 0%<span class="token punctuation">,</span> 0.502 4.45%<span class="token punctuation">,</span> 0.4903 8.76%<span class="token punctuation">,</span> 0.5064 12.93%<span class="token punctuation">,</span> 0.5439 17.11%<span class="token punctuation">,</span> 0.6285 23.64%<span class="token punctuation">,</span> 0.7794 33.8%<span class="token punctuation">,</span> 0.8568 38.11%<span class="token punctuation">,</span> 0.9106 42.42%<span class="token punctuation">,</span> 0.9148 45.62%<span class="token punctuation">,</span> 0.8758 49.79%<span class="token punctuation">,</span> 0.8078 54.24%<span class="token punctuation">,</span> 0.7361 58.97%<span class="token punctuation">,</span> 0.6636 64.53%<span class="token punctuation">,</span> 0.5677 70.1%<span class="token punctuation">,</span> 0.565 70.24%<span class="token punctuation">,</span> 0.565 70.24%<span class="token punctuation">,</span> 0.5317 72.88%<span class="token punctuation">,</span> 0.5152 76.22%<span class="token punctuation">,</span> 0.5237 80.81%<span class="token punctuation">,</span> 0.5823 86.09%<span class="token punctuation">,</span> 0.6435 90.68%<span class="token punctuation">,</span> 0.6668 94.85%<span class="token punctuation">,</span> 0.6559 97.91%<span class="token punctuation">,</span> 0.6434 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.6386 0%<span class="token punctuation">,</span> 0.6381 4.45%<span class="token punctuation">,</span> 0.6929 8.76%<span class="token punctuation">,</span> 0.7417 12.93%<span class="token punctuation">,</span> 0.7224 17.11%<span class="token punctuation">,</span> 0.6184 23.64%<span class="token punctuation">,</span> 0.3947 33.8%<span class="token punctuation">,</span> 0.3393 38.11%<span class="token punctuation">,</span> 0.342 42.42%<span class="token punctuation">,</span> 0.3679 45.62%<span class="token punctuation">,</span> 0.4001 49.79%<span class="token punctuation">,</span> 0.4495 54.24%<span class="token punctuation">,</span> 0.5692 58.97%<span class="token punctuation">,</span> 0.7689 64.53%<span class="token punctuation">,</span> 0.9941 70.1%<span class="token punctuation">,</span> 1 70.24%<span class="token punctuation">,</span> 0.0086 70.24%<span class="token punctuation">,</span> 0.1101 72.88%<span class="token punctuation">,</span> 0.1916 76.22%<span class="token punctuation">,</span> 0.232 80.81%<span class="token punctuation">,</span> 0.2762 86.09%<span class="token punctuation">,</span> 0.3657 90.68%<span class="token punctuation">,</span> 0.4742 94.85%<span class="token punctuation">,</span> 0.5181 97.91%<span class="token punctuation">,</span> 0.5193 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0.667<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-49</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.6421 0%<span class="token punctuation">,</span> 0.5861 5.01%<span class="token punctuation">,</span> 0.5046 10.01%<span class="token punctuation">,</span> 0.441 13.35%<span class="token punctuation">,</span> 0.4007 16.69%<span class="token punctuation">,</span> 0.3985 21%<span class="token punctuation">,</span> 0.4443 25.17%<span class="token punctuation">,</span> 0.5326 29.49%<span class="token punctuation">,</span> 0.7791 39.78%<span class="token punctuation">,</span> 0.9182 46.18%<span class="token punctuation">,</span> 0.9724 50.21%<span class="token punctuation">,</span> 0.99 54.1%<span class="token punctuation">,</span> 0.9801 59.39%<span class="token punctuation">,</span> 0.9604 62.59%<span class="token punctuation">,</span> 0.9592 62.73%<span class="token punctuation">,</span> 0.9592 62.73%<span class="token punctuation">,</span> 0.9471 66.34%<span class="token punctuation">,</span> 0.9782 70.79%<span class="token punctuation">,</span> 0.9957 72.46%<span class="token punctuation">,</span> 0.9974 72.6%<span class="token punctuation">,</span> 0.004 72.6%<span class="token punctuation">,</span> 0.0386 75.94%<span class="token punctuation">,</span> 0.0639 79.83%<span class="token punctuation">,</span> 0.0491 83.59%<span class="token punctuation">,</span> 0.0047 87.9%<span class="token punctuation">,</span> 0.0029 88.04%<span class="token punctuation">,</span> 0.9964 88.04%<span class="token punctuation">,</span> 0.9788 90.96%<span class="token punctuation">,</span> 0.9926 96.94%<span class="token punctuation">,</span> 0.9942 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5183 0%<span class="token punctuation">,</span> 0.4508 5.01%<span class="token punctuation">,</span> 0.3516 10.01%<span class="token punctuation">,</span> 0.3175 13.35%<span class="token punctuation">,</span> 0.3289 16.69%<span class="token punctuation">,</span> 0.3577 21%<span class="token punctuation">,</span> 0.387 25.17%<span class="token punctuation">,</span> 0.3985 29.49%<span class="token punctuation">,</span> 0.4439 39.78%<span class="token punctuation">,</span> 0.5327 46.18%<span class="token punctuation">,</span> 0.6291 50.21%<span class="token punctuation">,</span> 0.7441 54.1%<span class="token punctuation">,</span> 0.9059 59.39%<span class="token punctuation">,</span> 0.9941 62.59%<span class="token punctuation">,</span> 0.998 62.73%<span class="token punctuation">,</span> 0.0066 62.73%<span class="token punctuation">,</span> 0.1117 66.34%<span class="token punctuation">,</span> 0.2503 70.79%<span class="token punctuation">,</span> 0.2946 72.46%<span class="token punctuation">,</span> 0.2979 72.6%<span class="token punctuation">,</span> 0.2979 72.6%<span class="token punctuation">,</span> 0.3425 75.94%<span class="token punctuation">,</span> 0.3288 79.83%<span class="token punctuation">,</span> 0.316 83.59%<span class="token punctuation">,</span> 0.3352 87.9%<span class="token punctuation">,</span> 0.3361 88.04%<span class="token punctuation">,</span> 0.3361 88.04%<span class="token punctuation">,</span> 0.3505 90.96%<span class="token punctuation">,</span> 0.4196 96.94%<span class="token punctuation">,</span> 0.4857 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.p-50</span> <span class="token punctuation">{</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.9942 0%<span class="token punctuation">,</span> 0.9871 5.29%<span class="token punctuation">,</span> 0.9969 7.93%<span class="token punctuation">,</span> 0.9976 8.07%<span class="token punctuation">,</span> 0.0041 8.07%<span class="token punctuation">,</span> 0.0138 12.38%<span class="token punctuation">,</span> 0.0043 13.91%<span class="token punctuation">,</span> 0.003 14.05%<span class="token punctuation">,</span> 0.9965 14.05%<span class="token punctuation">,</span> 0.9519 18.08%<span class="token punctuation">,</span> 0.8796 22.95%<span class="token punctuation">,</span> 0.8209 27.12%<span class="token punctuation">,</span> 0.8015 30.46%<span class="token punctuation">,</span> 0.8104 34.35%<span class="token punctuation">,</span> 0.8602 39.5%<span class="token punctuation">,</span> 0.9145 42.98%<span class="token punctuation">,</span> 0.9651 46.73%<span class="token punctuation">,</span> 0.9651 49.79%<span class="token punctuation">,</span> 0.9278 53.27%<span class="token punctuation">,</span> 0.8536 57.02%<span class="token punctuation">,</span> 0.8156 59.94%<span class="token punctuation">,</span> 0.8121 64.53%<span class="token punctuation">,</span> 0.8149 69.68%<span class="token punctuation">,</span> 0.784 73.3%<span class="token punctuation">,</span> 0.7348 76.36%<span class="token punctuation">,</span> 0.6657 80.67%<span class="token punctuation">,</span> 0.5836 85.54%<span class="token punctuation">,</span> 0.501 91.38%<span class="token punctuation">,</span> 0.4407 97.22%<span class="token punctuation">,</span> 0.3961 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4894 0%<span class="token punctuation">,</span> 0.6269 5.29%<span class="token punctuation">,</span> 0.6635 7.93%<span class="token punctuation">,</span> 0.6651 8.07%<span class="token punctuation">,</span> 0.6651 8.07%<span class="token punctuation">,</span> 0.7487 12.38%<span class="token punctuation">,</span> 0.7855 13.91%<span class="token punctuation">,</span> 0.7893 14.05%<span class="token punctuation">,</span> 0.7893 14.05%<span class="token punctuation">,</span> 0.8689 18.08%<span class="token punctuation">,</span> 0.8906 22.95%<span class="token punctuation">,</span> 0.8744 27.12%<span class="token punctuation">,</span> 0.8067 30.46%<span class="token punctuation">,</span> 0.6904 34.35%<span class="token punctuation">,</span> 0.537 39.5%<span class="token punctuation">,</span> 0.4689 42.98%<span class="token punctuation">,</span> 0.443 46.73%<span class="token punctuation">,</span> 0.4312 49.79%<span class="token punctuation">,</span> 0.3948 53.27%<span class="token punctuation">,</span> 0.3682 57.02%<span class="token punctuation">,</span> 0.3933 59.94%<span class="token punctuation">,</span> 0.4668 64.53%<span class="token punctuation">,</span> 0.5963 69.68%<span class="token punctuation">,</span> 0.7025 73.3%<span class="token punctuation">,</span> 0.7625 76.36%<span class="token punctuation">,</span> 0.7681 80.67%<span class="token punctuation">,</span> 0.6865 85.54%<span class="token punctuation">,</span> 0.5165 91.38%<span class="token punctuation">,</span> 0.3421 97.22%<span class="token punctuation">,</span> 0.2632 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.667<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <pre><code><span class="token atrule"><span class="token rule">@scope</span></span> <span class="token punctuation">{</span> <span class="token selector">:scope</span> <span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> 400px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 400px<span class="token punctuation">;</span> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span> <span class="token property">background</span><span class="token punctuation">:</span> #fbfefa<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 8px<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> conf-x</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> -74.77px 0 <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> <span class="token property">translate</span><span class="token punctuation">:</span> 559.33px 0 <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> conf-y</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>-253.89px<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>617.26px<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> conf-r</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> <span class="token property">rotate</span><span class="token punctuation">:</span> -11.9rad <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> <span class="token property">rotate</span><span class="token punctuation">:</span> 11.68rad <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.conf</span> <span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span> <span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">animation-duration</span><span class="token punctuation">:</span> 2.5s<span class="token punctuation">;</span> <span class="token property">animation-iteration-count</span><span class="token punctuation">:</span> infinite<span class="token punctuation">;</span> <span class="token property">animation-name</span><span class="token punctuation">:</span> conf-x<span class="token punctuation">,</span> conf-y<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> inherit<span class="token punctuation">;</span> <span class="token property">animation</span><span class="token punctuation">:</span> conf-r 2.5s infinite<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-0</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 11.8px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.5116 2.83%<span class="token punctuation">,</span> 0.5184 5.5%<span class="token punctuation">,</span> 0.5152 8%<span class="token punctuation">,</span> 0.5 10.5%<span class="token punctuation">,</span> 0.4683 13.5%<span class="token punctuation">,</span> 0.4161 17.67%<span class="token punctuation">,</span> 0.3777 21.17%<span class="token punctuation">,</span> 0.346 25%<span class="token punctuation">,</span> 0.3217 29.83%<span class="token punctuation">,</span> 0.3152 36.83%<span class="token punctuation">,</span> 0.3304 45.5%<span class="token punctuation">,</span> 0.3336 54%<span class="token punctuation">,</span> 0.3262 62%<span class="token punctuation">,</span> 0.3215 70%<span class="token punctuation">,</span> 0.3386 77.83%<span class="token punctuation">,</span> 0.3874 86.17%<span class="token punctuation">,</span> 0.4359 94.83%<span class="token punctuation">,</span> 0.4499 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.6004 2.83%<span class="token punctuation">,</span> 0.5181 5.5%<span class="token punctuation">,</span> 0.4531 8%<span class="token punctuation">,</span> 0.4005 10.5%<span class="token punctuation">,</span> 0.3558 13.5%<span class="token punctuation">,</span> 0.3256 17.67%<span class="token punctuation">,</span> 0.3211 21.17%<span class="token punctuation">,</span> 0.3291 25%<span class="token punctuation">,</span> 0.3502 29.83%<span class="token punctuation">,</span> 0.3876 36.83%<span class="token punctuation">,</span> 0.4232 45.5%<span class="token punctuation">,</span> 0.454 54%<span class="token punctuation">,</span> 0.4989 62%<span class="token punctuation">,</span> 0.5621 70%<span class="token punctuation">,</span> 0.631 77.83%<span class="token punctuation">,</span> 0.6875 86.17%<span class="token punctuation">,</span> 0.717 94.83%<span class="token punctuation">,</span> 0.7308 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #e74c3c<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.5031 2.83%<span class="token punctuation">,</span> 0.4972 5.5%<span class="token punctuation">,</span> 0.4879 8%<span class="token punctuation">,</span> 0.4752 10.5%<span class="token punctuation">,</span> 0.4572 13.5%<span class="token punctuation">,</span> 0.4304 17.67%<span class="token punctuation">,</span> 0.4085 21.17%<span class="token punctuation">,</span> 0.3862 25%<span class="token punctuation">,</span> 0.3605 29.83%<span class="token punctuation">,</span> 0.3269 36.83%<span class="token punctuation">,</span> 0.2909 45.5%<span class="token punctuation">,</span> 0.2624 54%<span class="token punctuation">,</span> 0.2416 62%<span class="token punctuation">,</span> 0.2231 70%<span class="token punctuation">,</span> 0.202 77.83%<span class="token punctuation">,</span> 0.1749 86.17%<span class="token punctuation">,</span> 0.1454 94.83%<span class="token punctuation">,</span> 0.1296 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-1</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 20.24px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.4987 2.83%<span class="token punctuation">,</span> 0.5006 5.5%<span class="token punctuation">,</span> 0.5019 8%<span class="token punctuation">,</span> 0.5027 10.5%<span class="token punctuation">,</span> 0.5027 13.5%<span class="token punctuation">,</span> 0.5007 17.67%<span class="token punctuation">,</span> 0.4977 21.17%<span class="token punctuation">,</span> 0.4935 25%<span class="token punctuation">,</span> 0.4883 29.83%<span class="token punctuation">,</span> 0.4822 36.83%<span class="token punctuation">,</span> 0.4839 45.5%<span class="token punctuation">,</span> 0.4951 54%<span class="token punctuation">,</span> 0.5057 62%<span class="token punctuation">,</span> 0.5079 70%<span class="token punctuation">,</span> 0.4979 77.83%<span class="token punctuation">,</span> 0.4744 86.17%<span class="token punctuation">,</span> 0.4368 94.83%<span class="token punctuation">,</span> 0.4073 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.6567 2.83%<span class="token punctuation">,</span> 0.6173 5.5%<span class="token punctuation">,</span> 0.5851 8%<span class="token punctuation">,</span> 0.5571 10.5%<span class="token punctuation">,</span> 0.5289 13.5%<span class="token punctuation">,</span> 0.4985 17.67%<span class="token punctuation">,</span> 0.4806 21.17%<span class="token punctuation">,</span> 0.4682 25%<span class="token punctuation">,</span> 0.4625 29.83%<span class="token punctuation">,</span> 0.4709 36.83%<span class="token punctuation">,</span> 0.4974 45.5%<span class="token punctuation">,</span> 0.5266 54%<span class="token punctuation">,</span> 0.5522 62%<span class="token punctuation">,</span> 0.5782 70%<span class="token punctuation">,</span> 0.6087 77.83%<span class="token punctuation">,</span> 0.6507 86.17%<span class="token punctuation">,</span> 0.7039 94.83%<span class="token punctuation">,</span> 0.7371 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #3498db<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.504 2.83%<span class="token punctuation">,</span> 0.5028 5.5%<span class="token punctuation">,</span> 0.5012 8%<span class="token punctuation">,</span> 0.499 10.5%<span class="token punctuation">,</span> 0.4957 13.5%<span class="token punctuation">,</span> 0.4901 17.67%<span class="token punctuation">,</span> 0.4849 21.17%<span class="token punctuation">,</span> 0.4791 25%<span class="token punctuation">,</span> 0.4721 29.83%<span class="token punctuation">,</span> 0.4627 36.83%<span class="token punctuation">,</span> 0.4513 45.5%<span class="token punctuation">,</span> 0.4395 54%<span class="token punctuation">,</span> 0.4281 62%<span class="token punctuation">,</span> 0.4181 70%<span class="token punctuation">,</span> 0.4111 77.83%<span class="token punctuation">,</span> 0.4083 86.17%<span class="token punctuation">,</span> 0.412 94.83%<span class="token punctuation">,</span> 0.4181 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-2</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 15.35px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.5195 2.83%<span class="token punctuation">,</span> 0.5453 5.5%<span class="token punctuation">,</span> 0.5895 8%<span class="token punctuation">,</span> 0.6478 10.5%<span class="token punctuation">,</span> 0.7068 13.5%<span class="token punctuation">,</span> 0.7476 17.67%<span class="token punctuation">,</span> 0.7592 21.17%<span class="token punctuation">,</span> 0.765 25%<span class="token punctuation">,</span> 0.7734 29.83%<span class="token punctuation">,</span> 0.787 36.83%<span class="token punctuation">,</span> 0.7821 45.5%<span class="token punctuation">,</span> 0.7498 54%<span class="token punctuation">,</span> 0.7349 62%<span class="token punctuation">,</span> 0.7348 70%<span class="token punctuation">,</span> 0.7333 77.83%<span class="token punctuation">,</span> 0.7067 86.17%<span class="token punctuation">,</span> 0.6543 94.83%<span class="token punctuation">,</span> 0.6305 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.5797 2.83%<span class="token punctuation">,</span> 0.4813 5.5%<span class="token punctuation">,</span> 0.4128 8%<span class="token punctuation">,</span> 0.3771 10.5%<span class="token punctuation">,</span> 0.3709 13.5%<span class="token punctuation">,</span> 0.388 17.67%<span class="token punctuation">,</span> 0.4037 21.17%<span class="token punctuation">,</span> 0.4177 25%<span class="token punctuation">,</span> 0.4359 29.83%<span class="token punctuation">,</span> 0.4742 36.83%<span class="token punctuation">,</span> 0.5348 45.5%<span class="token punctuation">,</span> 0.575 54%<span class="token punctuation">,</span> 0.6012 62%<span class="token punctuation">,</span> 0.6421 70%<span class="token punctuation">,</span> 0.701 77.83%<span class="token punctuation">,</span> 0.7687 86.17%<span class="token punctuation">,</span> 0.8146 94.83%<span class="token punctuation">,</span> 0.8294 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #2ecc71<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.5147 2.83%<span class="token punctuation">,</span> 0.5286 5.5%<span class="token punctuation">,</span> 0.5502 8%<span class="token punctuation">,</span> 0.5774 10.5%<span class="token punctuation">,</span> 0.6114 13.5%<span class="token punctuation">,</span> 0.6541 17.67%<span class="token punctuation">,</span> 0.6846 21.17%<span class="token punctuation">,</span> 0.7131 25%<span class="token punctuation">,</span> 0.7428 29.83%<span class="token punctuation">,</span> 0.7774 36.83%<span class="token punctuation">,</span> 0.816 45.5%<span class="token punctuation">,</span> 0.8526 54%<span class="token punctuation">,</span> 0.8819 62%<span class="token punctuation">,</span> 0.9046 70%<span class="token punctuation">,</span> 0.9244 77.83%<span class="token punctuation">,</span> 0.9498 86.17%<span class="token punctuation">,</span> 0.9813 94.83%<span class="token punctuation">,</span> 1 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-3</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 17.95px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.4787 2.83%<span class="token punctuation">,</span> 0.4703 5.5%<span class="token punctuation">,</span> 0.469 8%<span class="token punctuation">,</span> 0.4741 10.5%<span class="token punctuation">,</span> 0.4871 13.5%<span class="token punctuation">,</span> 0.5119 17.67%<span class="token punctuation">,</span> 0.5336 21.17%<span class="token punctuation">,</span> 0.5556 25%<span class="token punctuation">,</span> 0.5805 29.83%<span class="token punctuation">,</span> 0.6097 36.83%<span class="token punctuation">,</span> 0.6218 45.5%<span class="token punctuation">,</span> 0.5957 54%<span class="token punctuation">,</span> 0.5656 62%<span class="token punctuation">,</span> 0.5563 70%<span class="token punctuation">,</span> 0.5579 77.83%<span class="token punctuation">,</span> 0.5561 86.17%<span class="token punctuation">,</span> 0.5246 94.83%<span class="token punctuation">,</span> 0.4907 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.6445 2.83%<span class="token punctuation">,</span> 0.5953 5.5%<span class="token punctuation">,</span> 0.5555 8%<span class="token punctuation">,</span> 0.5223 10.5%<span class="token punctuation">,</span> 0.4917 13.5%<span class="token punctuation">,</span> 0.4654 17.67%<span class="token punctuation">,</span> 0.4552 21.17%<span class="token punctuation">,</span> 0.4532 25%<span class="token punctuation">,</span> 0.4611 29.83%<span class="token punctuation">,</span> 0.4899 36.83%<span class="token punctuation">,</span> 0.5465 45.5%<span class="token punctuation">,</span> 0.5986 54%<span class="token punctuation">,</span> 0.6276 62%<span class="token punctuation">,</span> 0.6553 70%<span class="token punctuation">,</span> 0.6999 77.83%<span class="token punctuation">,</span> 0.7672 86.17%<span class="token punctuation">,</span> 0.8413 94.83%<span class="token punctuation">,</span> 0.8729 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #f1c40f<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.5068 2.83%<span class="token punctuation">,</span> 0.5124 5.5%<span class="token punctuation">,</span> 0.52 8%<span class="token punctuation">,</span> 0.5294 10.5%<span class="token punctuation">,</span> 0.5423 13.5%<span class="token punctuation">,</span> 0.5612 17.67%<span class="token punctuation">,</span> 0.5768 21.17%<span class="token punctuation">,</span> 0.5929 25%<span class="token punctuation">,</span> 0.6117 29.83%<span class="token punctuation">,</span> 0.6366 36.83%<span class="token punctuation">,</span> 0.667 45.5%<span class="token punctuation">,</span> 0.6991 54%<span class="token punctuation">,</span> 0.7289 62%<span class="token punctuation">,</span> 0.7537 70%<span class="token punctuation">,</span> 0.7724 77.83%<span class="token punctuation">,</span> 0.7912 86.17%<span class="token punctuation">,</span> 0.8177 94.83%<span class="token punctuation">,</span> 0.8372 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-4</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 8.35px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.4823 2.83%<span class="token punctuation">,</span> 0.4709 5.5%<span class="token punctuation">,</span> 0.4613 8%<span class="token punctuation">,</span> 0.4516 10.5%<span class="token punctuation">,</span> 0.439 13.5%<span class="token punctuation">,</span> 0.4193 17.67%<span class="token punctuation">,</span> 0.4018 21.17%<span class="token punctuation">,</span> 0.3828 25%<span class="token punctuation">,</span> 0.3604 29.83%<span class="token punctuation">,</span> 0.3311 36.83%<span class="token punctuation">,</span> 0.2991 45.5%<span class="token punctuation">,</span> 0.2667 54%<span class="token punctuation">,</span> 0.2319 62%<span class="token punctuation">,</span> 0.1925 70%<span class="token punctuation">,</span> 0.1496 77.83%<span class="token punctuation">,</span> 0.0989 86.17%<span class="token punctuation">,</span> 0.0392 94.83%<span class="token punctuation">,</span> 0 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.6305 2.83%<span class="token punctuation">,</span> 0.5726 5.5%<span class="token punctuation">,</span> 0.5269 8%<span class="token punctuation">,</span> 0.4883 10.5%<span class="token punctuation">,</span> 0.4502 13.5%<span class="token punctuation">,</span> 0.4104 17.67%<span class="token punctuation">,</span> 0.3871 21.17%<span class="token punctuation">,</span> 0.3706 25%<span class="token punctuation">,</span> 0.3606 29.83%<span class="token punctuation">,</span> 0.364 36.83%<span class="token punctuation">,</span> 0.3868 45.5%<span class="token punctuation">,</span> 0.4196 54%<span class="token punctuation">,</span> 0.4588 62%<span class="token punctuation">,</span> 0.5063 70%<span class="token punctuation">,</span> 0.5598 77.83%<span class="token punctuation">,</span> 0.6214 86.17%<span class="token punctuation">,</span> 0.6852 94.83%<span class="token punctuation">,</span> 0.72 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #9b59b6<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.5001 2.83%<span class="token punctuation">,</span> 0.4963 5.5%<span class="token punctuation">,</span> 0.4926 8%<span class="token punctuation">,</span> 0.4885 10.5%<span class="token punctuation">,</span> 0.4832 13.5%<span class="token punctuation">,</span> 0.4751 17.67%<span class="token punctuation">,</span> 0.4682 21.17%<span class="token punctuation">,</span> 0.4608 25%<span class="token punctuation">,</span> 0.4518 29.83%<span class="token punctuation">,</span> 0.44 36.83%<span class="token punctuation">,</span> 0.4277 45.5%<span class="token punctuation">,</span> 0.4184 54%<span class="token punctuation">,</span> 0.4122 62%<span class="token punctuation">,</span> 0.4085 70%<span class="token punctuation">,</span> 0.4075 77.83%<span class="token punctuation">,</span> 0.4096 86.17%<span class="token punctuation">,</span> 0.4157 94.83%<span class="token punctuation">,</span> 0.4211 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-5</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 15.06px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.5096 2.83%<span class="token punctuation">,</span> 0.5209 5.5%<span class="token punctuation">,</span> 0.5317 8%<span class="token punctuation">,</span> 0.5438 10.5%<span class="token punctuation">,</span> 0.5608 13.5%<span class="token punctuation">,</span> 0.5868 17.67%<span class="token punctuation">,</span> 0.6088 21.17%<span class="token punctuation">,</span> 0.6317 25%<span class="token punctuation">,</span> 0.6585 29.83%<span class="token punctuation">,</span> 0.6936 36.83%<span class="token punctuation">,</span> 0.7299 45.5%<span class="token punctuation">,</span> 0.7438 54%<span class="token punctuation">,</span> 0.7174 62%<span class="token punctuation">,</span> 0.6702 70%<span class="token punctuation">,</span> 0.643 77.83%<span class="token punctuation">,</span> 0.6362 86.17%<span class="token punctuation">,</span> 0.6311 94.83%<span class="token punctuation">,</span> 0.6187 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.6538 2.83%<span class="token punctuation">,</span> 0.6124 5.5%<span class="token punctuation">,</span> 0.579 8%<span class="token punctuation">,</span> 0.5509 10.5%<span class="token punctuation">,</span> 0.5244 13.5%<span class="token punctuation">,</span> 0.5 17.67%<span class="token punctuation">,</span> 0.4891 21.17%<span class="token punctuation">,</span> 0.4852 25%<span class="token punctuation">,</span> 0.49 29.83%<span class="token punctuation">,</span> 0.5134 36.83%<span class="token punctuation">,</span> 0.5662 45.5%<span class="token punctuation">,</span> 0.638 54%<span class="token punctuation">,</span> 0.7042 62%<span class="token punctuation">,</span> 0.7438 70%<span class="token punctuation">,</span> 0.7659 77.83%<span class="token punctuation">,</span> 0.802 86.17%<span class="token punctuation">,</span> 0.8626 94.83%<span class="token punctuation">,</span> 0.9047 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #e67e22<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.5122 2.83%<span class="token punctuation">,</span> 0.5186 5.5%<span class="token punctuation">,</span> 0.5251 8%<span class="token punctuation">,</span> 0.5324 10.5%<span class="token punctuation">,</span> 0.542 13.5%<span class="token punctuation">,</span> 0.5561 17.67%<span class="token punctuation">,</span> 0.5679 21.17%<span class="token punctuation">,</span> 0.5803 25%<span class="token punctuation">,</span> 0.5949 29.83%<span class="token punctuation">,</span> 0.614 36.83%<span class="token punctuation">,</span> 0.6356 45.5%<span class="token punctuation">,</span> 0.659 54%<span class="token punctuation">,</span> 0.6868 62%<span class="token punctuation">,</span> 0.7182 70%<span class="token punctuation">,</span> 0.7465 77.83%<span class="token punctuation">,</span> 0.7704 86.17%<span class="token punctuation">,</span> 0.7915 94.83%<span class="token punctuation">,</span> 0.8057 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-6</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 9.56px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.5011 2.83%<span class="token punctuation">,</span> 0.507 5.5%<span class="token punctuation">,</span> 0.518 8%<span class="token punctuation">,</span> 0.5357 10.5%<span class="token punctuation">,</span> 0.5643 13.5%<span class="token punctuation">,</span> 0.609 17.67%<span class="token punctuation">,</span> 0.645 21.17%<span class="token punctuation">,</span> 0.6796 25%<span class="token punctuation">,</span> 0.7152 29.83%<span class="token punctuation">,</span> 0.749 36.83%<span class="token punctuation">,</span> 0.7526 45.5%<span class="token punctuation">,</span> 0.7219 54%<span class="token punctuation">,</span> 0.6936 62%<span class="token punctuation">,</span> 0.6852 70%<span class="token punctuation">,</span> 0.6888 77.83%<span class="token punctuation">,</span> 0.6931 86.17%<span class="token punctuation">,</span> 0.683 94.83%<span class="token punctuation">,</span> 0.6615 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.6135 2.83%<span class="token punctuation">,</span> 0.5425 5.5%<span class="token punctuation">,</span> 0.4874 8%<span class="token punctuation">,</span> 0.4431 10.5%<span class="token punctuation">,</span> 0.4041 13.5%<span class="token punctuation">,</span> 0.3726 17.67%<span class="token punctuation">,</span> 0.3621 21.17%<span class="token punctuation">,</span> 0.3624 25%<span class="token punctuation">,</span> 0.3754 29.83%<span class="token punctuation">,</span> 0.4123 36.83%<span class="token punctuation">,</span> 0.4716 45.5%<span class="token punctuation">,</span> 0.5188 54%<span class="token punctuation">,</span> 0.5457 62%<span class="token punctuation">,</span> 0.5728 70%<span class="token punctuation">,</span> 0.615 77.83%<span class="token punctuation">,</span> 0.6788 86.17%<span class="token punctuation">,</span> 0.758 94.83%<span class="token punctuation">,</span> 0.8038 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #1abc9c<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.5104 2.83%<span class="token punctuation">,</span> 0.5171 5.5%<span class="token punctuation">,</span> 0.5253 8%<span class="token punctuation">,</span> 0.535 10.5%<span class="token punctuation">,</span> 0.5481 13.5%<span class="token punctuation">,</span> 0.5671 17.67%<span class="token punctuation">,</span> 0.5828 21.17%<span class="token punctuation">,</span> 0.5993 25%<span class="token punctuation">,</span> 0.6186 29.83%<span class="token punctuation">,</span> 0.6444 36.83%<span class="token punctuation">,</span> 0.6746 45.5%<span class="token punctuation">,</span> 0.7032 54%<span class="token punctuation">,</span> 0.7279 62%<span class="token punctuation">,</span> 0.7488 70%<span class="token punctuation">,</span> 0.7653 77.83%<span class="token punctuation">,</span> 0.7804 86.17%<span class="token punctuation">,</span> 0.7976 94.83%<span class="token punctuation">,</span> 0.8099 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-7</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 21.38px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.5028 2.83%<span class="token punctuation">,</span> 0.4784 5.5%<span class="token punctuation">,</span> 0.4361 8%<span class="token punctuation">,</span> 0.397 10.5%<span class="token punctuation">,</span> 0.3665 13.5%<span class="token punctuation">,</span> 0.3463 17.67%<span class="token punctuation">,</span> 0.3382 21.17%<span class="token punctuation">,</span> 0.3311 25%<span class="token punctuation">,</span> 0.3205 29.83%<span class="token punctuation">,</span> 0.3057 36.83%<span class="token punctuation">,</span> 0.3133 45.5%<span class="token punctuation">,</span> 0.3482 54%<span class="token punctuation">,</span> 0.3636 62%<span class="token punctuation">,</span> 0.3646 70%<span class="token punctuation">,</span> 0.3686 77.83%<span class="token punctuation">,</span> 0.401 86.17%<span class="token punctuation">,</span> 0.4521 94.83%<span class="token punctuation">,</span> 0.4704 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.6102 2.83%<span class="token punctuation">,</span> 0.5422 5.5%<span class="token punctuation">,</span> 0.5077 8%<span class="token punctuation">,</span> 0.4979 10.5%<span class="token punctuation">,</span> 0.502 13.5%<span class="token punctuation">,</span> 0.5147 17.67%<span class="token punctuation">,</span> 0.5257 21.17%<span class="token punctuation">,</span> 0.5377 25%<span class="token punctuation">,</span> 0.5569 29.83%<span class="token punctuation">,</span> 0.5981 36.83%<span class="token punctuation">,</span> 0.6608 45.5%<span class="token punctuation">,</span> 0.7009 54%<span class="token punctuation">,</span> 0.727 62%<span class="token punctuation">,</span> 0.7689 70%<span class="token punctuation">,</span> 0.8287 77.83%<span class="token punctuation">,</span> 0.8939 86.17%<span class="token punctuation">,</span> 0.933 94.83%<span class="token punctuation">,</span> 0.9469 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #ff6b9d<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.4881 2.83%<span class="token punctuation">,</span> 0.4628 5.5%<span class="token punctuation">,</span> 0.4344 8%<span class="token punctuation">,</span> 0.4059 10.5%<span class="token punctuation">,</span> 0.3746 13.5%<span class="token punctuation">,</span> 0.3374 17.67%<span class="token punctuation">,</span> 0.3111 21.17%<span class="token punctuation">,</span> 0.2865 25%<span class="token punctuation">,</span> 0.2605 29.83%<span class="token punctuation">,</span> 0.2295 36.83%<span class="token punctuation">,</span> 0.1922 45.5%<span class="token punctuation">,</span> 0.155 54%<span class="token punctuation">,</span> 0.1251 62%<span class="token punctuation">,</span> 0.1021 70%<span class="token punctuation">,</span> 0.0814 77.83%<span class="token punctuation">,</span> 0.0535 86.17%<span class="token punctuation">,</span> 0.0193 94.83%<span class="token punctuation">,</span> 0 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-8</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 15.15px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.5057 2.83%<span class="token punctuation">,</span> 0.4909 5.5%<span class="token punctuation">,</span> 0.4516 8%<span class="token punctuation">,</span> 0.4042 10.5%<span class="token punctuation">,</span> 0.3598 13.5%<span class="token punctuation">,</span> 0.3277 17.67%<span class="token punctuation">,</span> 0.317 21.17%<span class="token punctuation">,</span> 0.3115 25%<span class="token punctuation">,</span> 0.3044 29.83%<span class="token punctuation">,</span> 0.2909 36.83%<span class="token punctuation">,</span> 0.2859 45.5%<span class="token punctuation">,</span> 0.3172 54%<span class="token punctuation">,</span> 0.3461 62%<span class="token punctuation">,</span> 0.353 70%<span class="token punctuation">,</span> 0.3528 77.83%<span class="token punctuation">,</span> 0.3627 86.17%<span class="token punctuation">,</span> 0.406 94.83%<span class="token punctuation">,</span> 0.4382 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.5945 2.83%<span class="token punctuation">,</span> 0.5105 5.5%<span class="token punctuation">,</span> 0.4576 8%<span class="token punctuation">,</span> 0.4337 10.5%<span class="token punctuation">,</span> 0.4312 13.5%<span class="token punctuation">,</span> 0.445 17.67%<span class="token punctuation">,</span> 0.4586 21.17%<span class="token punctuation">,</span> 0.4719 25%<span class="token punctuation">,</span> 0.4888 29.83%<span class="token punctuation">,</span> 0.5238 36.83%<span class="token punctuation">,</span> 0.586 45.5%<span class="token punctuation">,</span> 0.6383 54%<span class="token punctuation">,</span> 0.6654 62%<span class="token punctuation">,</span> 0.6959 70%<span class="token punctuation">,</span> 0.7446 77.83%<span class="token punctuation">,</span> 0.8129 86.17%<span class="token punctuation">,</span> 0.876 94.83%<span class="token punctuation">,</span> 0.8988 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #e74c3c<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.4947 2.83%<span class="token punctuation">,</span> 0.4759 5.5%<span class="token punctuation">,</span> 0.4515 8%<span class="token punctuation">,</span> 0.4246 10.5%<span class="token punctuation">,</span> 0.3934 13.5%<span class="token punctuation">,</span> 0.355 17.67%<span class="token punctuation">,</span> 0.3274 21.17%<span class="token punctuation">,</span> 0.3014 25%<span class="token punctuation">,</span> 0.2738 29.83%<span class="token punctuation">,</span> 0.2417 36.83%<span class="token punctuation">,</span> 0.207 45.5%<span class="token punctuation">,</span> 0.172 54%<span class="token punctuation">,</span> 0.141 62%<span class="token punctuation">,</span> 0.1159 70%<span class="token punctuation">,</span> 0.0964 77.83%<span class="token punctuation">,</span> 0.0749 86.17%<span class="token punctuation">,</span> 0.0463 94.83%<span class="token punctuation">,</span> 0.0273 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-9</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 14.76px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.5088 2.83%<span class="token punctuation">,</span> 0.4885 5.5%<span class="token punctuation">,</span> 0.4516 8%<span class="token punctuation">,</span> 0.4149 10.5%<span class="token punctuation">,</span> 0.3826 13.5%<span class="token punctuation">,</span> 0.3581 17.67%<span class="token punctuation">,</span> 0.3491 21.17%<span class="token punctuation">,</span> 0.3448 25%<span class="token punctuation">,</span> 0.3398 29.83%<span class="token punctuation">,</span> 0.3278 36.83%<span class="token punctuation">,</span> 0.3166 45.5%<span class="token punctuation">,</span> 0.3381 54%<span class="token punctuation">,</span> 0.3778 62%<span class="token punctuation">,</span> 0.3985 70%<span class="token punctuation">,</span> 0.4014 77.83%<span class="token punctuation">,</span> 0.4036 86.17%<span class="token punctuation">,</span> 0.4284 94.83%<span class="token punctuation">,</span> 0.4589 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.6139 2.83%<span class="token punctuation">,</span> 0.5499 5.5%<span class="token punctuation">,</span> 0.5156 8%<span class="token punctuation">,</span> 0.5028 10.5%<span class="token punctuation">,</span> 0.5036 13.5%<span class="token punctuation">,</span> 0.5159 17.67%<span class="token punctuation">,</span> 0.5285 21.17%<span class="token punctuation">,</span> 0.5417 25%<span class="token punctuation">,</span> 0.558 29.83%<span class="token punctuation">,</span> 0.5899 36.83%<span class="token punctuation">,</span> 0.6502 45.5%<span class="token punctuation">,</span> 0.7139 54%<span class="token punctuation">,</span> 0.7505 62%<span class="token punctuation">,</span> 0.7745 70%<span class="token punctuation">,</span> 0.8113 77.83%<span class="token punctuation">,</span> 0.8713 86.17%<span class="token punctuation">,</span> 0.943 94.83%<span class="token punctuation">,</span> 0.977 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #3498db<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.4881 2.83%<span class="token punctuation">,</span> 0.4648 5.5%<span class="token punctuation">,</span> 0.4398 8%<span class="token punctuation">,</span> 0.4149 10.5%<span class="token punctuation">,</span> 0.3872 13.5%<span class="token punctuation">,</span> 0.3533 17.67%<span class="token punctuation">,</span> 0.3287 21.17%<span class="token punctuation">,</span> 0.3051 25%<span class="token punctuation">,</span> 0.2797 29.83%<span class="token punctuation">,</span> 0.2501 36.83%<span class="token punctuation">,</span> 0.2194 45.5%<span class="token punctuation">,</span> 0.1876 54%<span class="token punctuation">,</span> 0.1562 62%<span class="token punctuation">,</span> 0.1284 70%<span class="token punctuation">,</span> 0.1069 77.83%<span class="token punctuation">,</span> 0.0873 86.17%<span class="token punctuation">,</span> 0.063 94.83%<span class="token punctuation">,</span> 0.0453 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-10</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 16.69px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.5188 2.83%<span class="token punctuation">,</span> 0.5102 5.5%<span class="token punctuation">,</span> 0.4903 8%<span class="token punctuation">,</span> 0.469 10.5%<span class="token punctuation">,</span> 0.4473 13.5%<span class="token punctuation">,</span> 0.4257 17.67%<span class="token punctuation">,</span> 0.4144 21.17%<span class="token punctuation">,</span> 0.4086 25%<span class="token punctuation">,</span> 0.4089 29.83%<span class="token punctuation">,</span> 0.4107 36.83%<span class="token punctuation">,</span> 0.4012 45.5%<span class="token punctuation">,</span> 0.3894 54%<span class="token punctuation">,</span> 0.3936 62%<span class="token punctuation">,</span> 0.433 70%<span class="token punctuation">,</span> 0.4802 77.83%<span class="token punctuation">,</span> 0.5026 86.17%<span class="token punctuation">,</span> 0.5085 94.83%<span class="token punctuation">,</span> 0.513 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.6414 2.83%<span class="token punctuation">,</span> 0.5978 5.5%<span class="token punctuation">,</span> 0.5729 8%<span class="token punctuation">,</span> 0.5608 10.5%<span class="token punctuation">,</span> 0.5568 13.5%<span class="token punctuation">,</span> 0.5623 17.67%<span class="token punctuation">,</span> 0.5727 21.17%<span class="token punctuation">,</span> 0.5872 25%<span class="token punctuation">,</span> 0.6058 29.83%<span class="token punctuation">,</span> 0.6297 36.83%<span class="token punctuation">,</span> 0.6683 45.5%<span class="token punctuation">,</span> 0.7281 54%<span class="token punctuation">,</span> 0.7993 62%<span class="token punctuation">,</span> 0.8614 70%<span class="token punctuation">,</span> 0.8933 77.83%<span class="token punctuation">,</span> 0.9173 86.17%<span class="token punctuation">,</span> 0.9622 94.83%<span class="token punctuation">,</span> 1 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #2ecc71<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.4867 2.83%<span class="token punctuation">,</span> 0.4653 5.5%<span class="token punctuation">,</span> 0.4443 8%<span class="token punctuation">,</span> 0.4242 10.5%<span class="token punctuation">,</span> 0.4018 13.5%<span class="token punctuation">,</span> 0.3741 17.67%<span class="token punctuation">,</span> 0.3534 21.17%<span class="token punctuation">,</span> 0.3329 25%<span class="token punctuation">,</span> 0.3096 29.83%<span class="token punctuation">,</span> 0.2808 36.83%<span class="token punctuation">,</span> 0.2533 45.5%<span class="token punctuation">,</span> 0.2319 54%<span class="token punctuation">,</span> 0.2097 62%<span class="token punctuation">,</span> 0.1809 70%<span class="token punctuation">,</span> 0.1495 77.83%<span class="token punctuation">,</span> 0.1201 86.17%<span class="token punctuation">,</span> 0.0965 94.83%<span class="token punctuation">,</span> 0.0835 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-11</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 21.56px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.4806 2.83%<span class="token punctuation">,</span> 0.4679 5.5%<span class="token punctuation">,</span> 0.4573 8%<span class="token punctuation">,</span> 0.4479 10.5%<span class="token punctuation">,</span> 0.4394 13.5%<span class="token punctuation">,</span> 0.4365 17.67%<span class="token punctuation">,</span> 0.4445 21.17%<span class="token punctuation">,</span> 0.462 25%<span class="token punctuation">,</span> 0.4889 29.83%<span class="token punctuation">,</span> 0.5255 36.83%<span class="token punctuation">,</span> 0.5641 45.5%<span class="token punctuation">,</span> 0.5877 54%<span class="token punctuation">,</span> 0.5769 62%<span class="token punctuation">,</span> 0.5403 70%<span class="token punctuation">,</span> 0.5167 77.83%<span class="token punctuation">,</span> 0.5127 86.17%<span class="token punctuation">,</span> 0.5115 94.83%<span class="token punctuation">,</span> 0.5025 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.6197 2.83%<span class="token punctuation">,</span> 0.549 5.5%<span class="token punctuation">,</span> 0.49 8%<span class="token punctuation">,</span> 0.4373 10.5%<span class="token punctuation">,</span> 0.3817 13.5%<span class="token punctuation">,</span> 0.3174 17.67%<span class="token punctuation">,</span> 0.2758 21.17%<span class="token punctuation">,</span> 0.2444 25%<span class="token punctuation">,</span> 0.2237 29.83%<span class="token punctuation">,</span> 0.2202 36.83%<span class="token punctuation">,</span> 0.2464 45.5%<span class="token punctuation">,</span> 0.2988 54%<span class="token punctuation">,</span> 0.3576 62%<span class="token punctuation">,</span> 0.3986 70%<span class="token punctuation">,</span> 0.4226 77.83%<span class="token punctuation">,</span> 0.459 86.17%<span class="token punctuation">,</span> 0.5204 94.83%<span class="token punctuation">,</span> 0.5647 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #f1c40f<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.5016 2.83%<span class="token punctuation">,</span> 0.5012 5.5%<span class="token punctuation">,</span> 0.502 8%<span class="token punctuation">,</span> 0.5041 10.5%<span class="token punctuation">,</span> 0.5088 13.5%<span class="token punctuation">,</span> 0.5198 17.67%<span class="token punctuation">,</span> 0.5324 21.17%<span class="token punctuation">,</span> 0.5483 25%<span class="token punctuation">,</span> 0.5687 29.83%<span class="token punctuation">,</span> 0.5962 36.83%<span class="token punctuation">,</span> 0.6257 45.5%<span class="token punctuation">,</span> 0.6531 54%<span class="token punctuation">,</span> 0.6819 62%<span class="token punctuation">,</span> 0.7137 70%<span class="token punctuation">,</span> 0.7425 77.83%<span class="token punctuation">,</span> 0.7664 86.17%<span class="token punctuation">,</span> 0.787 94.83%<span class="token punctuation">,</span> 0.8011 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-12</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 18.25px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.4917 2.83%<span class="token punctuation">,</span> 0.4982 5.5%<span class="token punctuation">,</span> 0.5146 8%<span class="token punctuation">,</span> 0.5362 10.5%<span class="token punctuation">,</span> 0.5624 13.5%<span class="token punctuation">,</span> 0.5928 17.67%<span class="token punctuation">,</span> 0.6119 21.17%<span class="token punctuation">,</span> 0.6258 25%<span class="token punctuation">,</span> 0.6321 29.83%<span class="token punctuation">,</span> 0.6241 36.83%<span class="token punctuation">,</span> 0.6159 45.5%<span class="token punctuation">,</span> 0.6229 54%<span class="token punctuation">,</span> 0.6312 62%<span class="token punctuation">,</span> 0.6247 70%<span class="token punctuation">,</span> 0.5844 77.83%<span class="token punctuation">,</span> 0.5323 86.17%<span class="token punctuation">,</span> 0.5098 94.83%<span class="token punctuation">,</span> 0.5055 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.646 2.83%<span class="token punctuation">,</span> 0.6004 5.5%<span class="token punctuation">,</span> 0.5688 8%<span class="token punctuation">,</span> 0.5487 10.5%<span class="token punctuation">,</span> 0.5371 13.5%<span class="token punctuation">,</span> 0.5361 17.67%<span class="token punctuation">,</span> 0.5439 21.17%<span class="token punctuation">,</span> 0.5585 25%<span class="token punctuation">,</span> 0.5816 29.83%<span class="token punctuation">,</span> 0.6126 36.83%<span class="token punctuation">,</span> 0.6426 45.5%<span class="token punctuation">,</span> 0.6835 54%<span class="token punctuation">,</span> 0.7423 62%<span class="token punctuation">,</span> 0.8152 70%<span class="token punctuation">,</span> 0.8777 77.83%<span class="token punctuation">,</span> 0.9117 86.17%<span class="token punctuation">,</span> 0.937 94.83%<span class="token punctuation">,</span> 0.962 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #9b59b6<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.5176 2.83%<span class="token punctuation">,</span> 0.5328 5.5%<span class="token punctuation">,</span> 0.549 8%<span class="token punctuation">,</span> 0.5658 10.5%<span class="token punctuation">,</span> 0.5857 13.5%<span class="token punctuation">,</span> 0.6113 17.67%<span class="token punctuation">,</span> 0.631 21.17%<span class="token punctuation">,</span> 0.6509 25%<span class="token punctuation">,</span> 0.6741 29.83%<span class="token punctuation">,</span> 0.7048 36.83%<span class="token punctuation">,</span> 0.7368 45.5%<span class="token punctuation">,</span> 0.7605 54%<span class="token punctuation">,</span> 0.7786 62%<span class="token punctuation">,</span> 0.7997 70%<span class="token punctuation">,</span> 0.8278 77.83%<span class="token punctuation">,</span> 0.8619 86.17%<span class="token punctuation">,</span> 0.8928 94.83%<span class="token punctuation">,</span> 0.9076 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-13</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 10.07px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.4897 2.83%<span class="token punctuation">,</span> 0.4726 5.5%<span class="token punctuation">,</span> 0.4433 8%<span class="token punctuation">,</span> 0.4083 10.5%<span class="token punctuation">,</span> 0.3683 13.5%<span class="token punctuation">,</span> 0.3253 17.67%<span class="token punctuation">,</span> 0.3015 21.17%<span class="token punctuation">,</span> 0.2872 25%<span class="token punctuation">,</span> 0.2839 29.83%<span class="token punctuation">,</span> 0.2942 36.83%<span class="token punctuation">,</span> 0.2994 45.5%<span class="token punctuation">,</span> 0.2915 54%<span class="token punctuation">,</span> 0.2856 62%<span class="token punctuation">,</span> 0.2997 70%<span class="token punctuation">,</span> 0.3421 77.83%<span class="token punctuation">,</span> 0.3928 86.17%<span class="token punctuation">,</span> 0.4202 94.83%<span class="token punctuation">,</span> 0.4242 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.6198 2.83%<span class="token punctuation">,</span> 0.5563 5.5%<span class="token punctuation">,</span> 0.5143 8%<span class="token punctuation">,</span> 0.4893 10.5%<span class="token punctuation">,</span> 0.4771 13.5%<span class="token punctuation">,</span> 0.4804 17.67%<span class="token punctuation">,</span> 0.4931 21.17%<span class="token punctuation">,</span> 0.512 25%<span class="token punctuation">,</span> 0.5374 29.83%<span class="token punctuation">,</span> 0.5671 36.83%<span class="token punctuation">,</span> 0.5975 45.5%<span class="token punctuation">,</span> 0.6423 54%<span class="token punctuation">,</span> 0.7031 62%<span class="token punctuation">,</span> 0.7724 70%<span class="token punctuation">,</span> 0.8284 77.83%<span class="token punctuation">,</span> 0.8622 86.17%<span class="token punctuation">,</span> 0.8854 94.83%<span class="token punctuation">,</span> 0.9052 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #e67e22<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.4906 2.83%<span class="token punctuation">,</span> 0.4748 5.5%<span class="token punctuation">,</span> 0.458 8%<span class="token punctuation">,</span> 0.4404 10.5%<span class="token punctuation">,</span> 0.4198 13.5%<span class="token punctuation">,</span> 0.393 17.67%<span class="token punctuation">,</span> 0.3725 21.17%<span class="token punctuation">,</span> 0.3519 25%<span class="token punctuation">,</span> 0.3283 29.83%<span class="token punctuation">,</span> 0.298 36.83%<span class="token punctuation">,</span> 0.2672 45.5%<span class="token punctuation">,</span> 0.2437 54%<span class="token punctuation">,</span> 0.2247 62%<span class="token punctuation">,</span> 0.2042 70%<span class="token punctuation">,</span> 0.1811 77.83%<span class="token punctuation">,</span> 0.1553 86.17%<span class="token punctuation">,</span> 0.1309 94.83%<span class="token punctuation">,</span> 0.1186 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-14</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 10.46px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.5255 2.83%<span class="token punctuation">,</span> 0.5404 5.5%<span class="token punctuation">,</span> 0.5472 8%<span class="token punctuation">,</span> 0.5469 10.5%<span class="token punctuation">,</span> 0.5375 13.5%<span class="token punctuation">,</span> 0.5126 17.67%<span class="token punctuation">,</span> 0.4872 21.17%<span class="token punctuation">,</span> 0.4598 25%<span class="token punctuation">,</span> 0.4284 29.83%<span class="token punctuation">,</span> 0.3896 36.83%<span class="token punctuation">,</span> 0.3557 45.5%<span class="token punctuation">,</span> 0.3538 54%<span class="token punctuation">,</span> 0.3854 62%<span class="token punctuation">,</span> 0.424 70%<span class="token punctuation">,</span> 0.4429 77.83%<span class="token punctuation">,</span> 0.4441 86.17%<span class="token punctuation">,</span> 0.4414 94.83%<span class="token punctuation">,</span> 0.4432 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.6173 2.83%<span class="token punctuation">,</span> 0.5481 5.5%<span class="token punctuation">,</span> 0.4929 8%<span class="token punctuation">,</span> 0.4462 10.5%<span class="token punctuation">,</span> 0.401 13.5%<span class="token punctuation">,</span> 0.3575 17.67%<span class="token punctuation">,</span> 0.3362 21.17%<span class="token punctuation">,</span> 0.3251 25%<span class="token punctuation">,</span> 0.3244 29.83%<span class="token punctuation">,</span> 0.3427 36.83%<span class="token punctuation">,</span> 0.391 45.5%<span class="token punctuation">,</span> 0.4546 54%<span class="token punctuation">,</span> 0.5067 62%<span class="token punctuation">,</span> 0.5381 70%<span class="token punctuation">,</span> 0.5609 77.83%<span class="token punctuation">,</span> 0.5985 86.17%<span class="token punctuation">,</span> 0.6595 94.83%<span class="token punctuation">,</span> 0.7045 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #1abc9c<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.5054 2.83%<span class="token punctuation">,</span> 0.5021 5.5%<span class="token punctuation">,</span> 0.4966 8%<span class="token punctuation">,</span> 0.4893 10.5%<span class="token punctuation">,</span> 0.4786 13.5%<span class="token punctuation">,</span> 0.462 17.67%<span class="token punctuation">,</span> 0.4477 21.17%<span class="token punctuation">,</span> 0.4326 25%<span class="token punctuation">,</span> 0.4147 29.83%<span class="token punctuation">,</span> 0.3914 36.83%<span class="token punctuation">,</span> 0.3651 45.5%<span class="token punctuation">,</span> 0.3394 54%<span class="token punctuation">,</span> 0.3137 62%<span class="token punctuation">,</span> 0.2882 70%<span class="token punctuation">,</span> 0.266 77.83%<span class="token punctuation">,</span> 0.2469 86.17%<span class="token punctuation">,</span> 0.2307 94.83%<span class="token punctuation">,</span> 0.2208 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-15</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 22.28px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.5022 2.83%<span class="token punctuation">,</span> 0.5004 5.5%<span class="token punctuation">,</span> 0.4887 8%<span class="token punctuation">,</span> 0.4684 10.5%<span class="token punctuation">,</span> 0.4387 13.5%<span class="token punctuation">,</span> 0.3998 17.67%<span class="token punctuation">,</span> 0.3742 21.17%<span class="token punctuation">,</span> 0.3543 25%<span class="token punctuation">,</span> 0.3417 29.83%<span class="token punctuation">,</span> 0.3454 36.83%<span class="token punctuation">,</span> 0.3553 45.5%<span class="token punctuation">,</span> 0.3497 54%<span class="token punctuation">,</span> 0.3414 62%<span class="token punctuation">,</span> 0.347 70%<span class="token punctuation">,</span> 0.3869 77.83%<span class="token punctuation">,</span> 0.4371 86.17%<span class="token punctuation">,</span> 0.4572 94.83%<span class="token punctuation">,</span> 0.4613 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.6381 2.83%<span class="token punctuation">,</span> 0.5837 5.5%<span class="token punctuation">,</span> 0.5418 8%<span class="token punctuation">,</span> 0.5107 10.5%<span class="token punctuation">,</span> 0.4882 13.5%<span class="token punctuation">,</span> 0.4782 17.67%<span class="token punctuation">,</span> 0.4822 21.17%<span class="token punctuation">,</span> 0.4945 25%<span class="token punctuation">,</span> 0.517 29.83%<span class="token punctuation">,</span> 0.55 36.83%<span class="token punctuation">,</span> 0.5807 45.5%<span class="token punctuation">,</span> 0.6191 54%<span class="token punctuation">,</span> 0.6757 62%<span class="token punctuation">,</span> 0.7474 70%<span class="token punctuation">,</span> 0.809 77.83%<span class="token punctuation">,</span> 0.8417 86.17%<span class="token punctuation">,</span> 0.8684 94.83%<span class="token punctuation">,</span> 0.8949 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #ff6b9d<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.4975 2.83%<span class="token punctuation">,</span> 0.487 5.5%<span class="token punctuation">,</span> 0.4738 8%<span class="token punctuation">,</span> 0.4584 10.5%<span class="token punctuation">,</span> 0.4387 13.5%<span class="token punctuation">,</span> 0.4118 17.67%<span class="token punctuation">,</span> 0.3907 21.17%<span class="token punctuation">,</span> 0.3692 25%<span class="token punctuation">,</span> 0.3443 29.83%<span class="token punctuation">,</span> 0.3113 36.83%<span class="token punctuation">,</span> 0.2765 45.5%<span class="token punctuation">,</span> 0.2508 54%<span class="token punctuation">,</span> 0.2316 62%<span class="token punctuation">,</span> 0.2096 70%<span class="token punctuation">,</span> 0.1803 77.83%<span class="token punctuation">,</span> 0.145 86.17%<span class="token punctuation">,</span> 0.1137 94.83%<span class="token punctuation">,</span> 0.0988 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-16</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 15.14px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.4965 2.83%<span class="token punctuation">,</span> 0.5036 5.5%<span class="token punctuation">,</span> 0.5232 8%<span class="token punctuation">,</span> 0.5542 10.5%<span class="token punctuation">,</span> 0.5962 13.5%<span class="token punctuation">,</span> 0.6448 17.67%<span class="token punctuation">,</span> 0.6715 21.17%<span class="token punctuation">,</span> 0.6874 25%<span class="token punctuation">,</span> 0.6925 29.83%<span class="token punctuation">,</span> 0.6885 36.83%<span class="token punctuation">,</span> 0.6937 45.5%<span class="token punctuation">,</span> 0.7041 54%<span class="token punctuation">,</span> 0.6997 62%<span class="token punctuation">,</span> 0.6622 70%<span class="token punctuation">,</span> 0.619 77.83%<span class="token punctuation">,</span> 0.5993 86.17%<span class="token punctuation">,</span> 0.5954 94.83%<span class="token punctuation">,</span> 0.5926 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.617 2.83%<span class="token punctuation">,</span> 0.5467 5.5%<span class="token punctuation">,</span> 0.4938 8%<span class="token punctuation">,</span> 0.4569 10.5%<span class="token punctuation">,</span> 0.4339 13.5%<span class="token punctuation">,</span> 0.4298 17.67%<span class="token punctuation">,</span> 0.4397 21.17%<span class="token punctuation">,</span> 0.4564 25%<span class="token punctuation">,</span> 0.4787 29.83%<span class="token punctuation">,</span> 0.5051 36.83%<span class="token punctuation">,</span> 0.5399 45.5%<span class="token punctuation">,</span> 0.5946 54%<span class="token punctuation">,</span> 0.6616 62%<span class="token punctuation">,</span> 0.7195 70%<span class="token punctuation">,</span> 0.75 77.83%<span class="token punctuation">,</span> 0.7749 86.17%<span class="token punctuation">,</span> 0.8206 94.83%<span class="token punctuation">,</span> 0.8589 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #e74c3c<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.5123 2.83%<span class="token punctuation">,</span> 0.5236 5.5%<span class="token punctuation">,</span> 0.5385 8%<span class="token punctuation">,</span> 0.5564 10.5%<span class="token punctuation">,</span> 0.5795 13.5%<span class="token punctuation">,</span> 0.6108 17.67%<span class="token punctuation">,</span> 0.6349 21.17%<span class="token punctuation">,</span> 0.659 25%<span class="token punctuation">,</span> 0.6863 29.83%<span class="token punctuation">,</span> 0.7201 36.83%<span class="token punctuation">,</span> 0.7528 45.5%<span class="token punctuation">,</span> 0.7776 54%<span class="token punctuation">,</span> 0.8014 62%<span class="token punctuation">,</span> 0.8301 70%<span class="token punctuation">,</span> 0.8601 77.83%<span class="token punctuation">,</span> 0.8879 86.17%<span class="token punctuation">,</span> 0.9102 94.83%<span class="token punctuation">,</span> 0.9224 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-17</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 23.83px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.5149 2.83%<span class="token punctuation">,</span> 0.5301 5.5%<span class="token punctuation">,</span> 0.5431 8%<span class="token punctuation">,</span> 0.555 10.5%<span class="token punctuation">,</span> 0.5664 13.5%<span class="token punctuation">,</span> 0.5649 17.67%<span class="token punctuation">,</span> 0.5357 21.17%<span class="token punctuation">,</span> 0.4876 25%<span class="token punctuation">,</span> 0.4385 29.83%<span class="token punctuation">,</span> 0.4057 36.83%<span class="token punctuation">,</span> 0.4026 45.5%<span class="token punctuation">,</span> 0.3976 54%<span class="token punctuation">,</span> 0.3861 62%<span class="token punctuation">,</span> 0.3839 70%<span class="token punctuation">,</span> 0.4151 77.83%<span class="token punctuation">,</span> 0.4617 86.17%<span class="token punctuation">,</span> 0.4812 94.83%<span class="token punctuation">,</span> 0.4845 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.5713 2.83%<span class="token punctuation">,</span> 0.4609 5.5%<span class="token punctuation">,</span> 0.3689 8%<span class="token punctuation">,</span> 0.2864 10.5%<span class="token punctuation">,</span> 0.1986 13.5%<span class="token punctuation">,</span> 0.0962 17.67%<span class="token punctuation">,</span> 0.0351 21.17%<span class="token punctuation">,</span> 0.003 25%<span class="token punctuation">,</span> 0 29.83%<span class="token punctuation">,</span> 0.0242 36.83%<span class="token punctuation">,</span> 0.0582 45.5%<span class="token punctuation">,</span> 0.0902 54%<span class="token punctuation">,</span> 0.1377 62%<span class="token punctuation">,</span> 0.2034 70%<span class="token punctuation">,</span> 0.2647 77.83%<span class="token punctuation">,</span> 0.2998 86.17%<span class="token punctuation">,</span> 0.3268 94.83%<span class="token punctuation">,</span> 0.3529 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #3498db<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.508 2.83%<span class="token punctuation">,</span> 0.5085 5.5%<span class="token punctuation">,</span> 0.5081 8%<span class="token punctuation">,</span> 0.5065 10.5%<span class="token punctuation">,</span> 0.5013 13.5%<span class="token punctuation">,</span> 0.4839 17.67%<span class="token punctuation">,</span> 0.4597 21.17%<span class="token punctuation">,</span> 0.4288 25%<span class="token punctuation">,</span> 0.3911 29.83%<span class="token punctuation">,</span> 0.3443 36.83%<span class="token punctuation">,</span> 0.2973 45.5%<span class="token punctuation">,</span> 0.2624 54%<span class="token punctuation">,</span> 0.2376 62%<span class="token punctuation">,</span> 0.2134 70%<span class="token punctuation">,</span> 0.1836 77.83%<span class="token punctuation">,</span> 0.1482 86.17%<span class="token punctuation">,</span> 0.1167 94.83%<span class="token punctuation">,</span> 0.1018 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-18</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 7.44px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.5293 2.83%<span class="token punctuation">,</span> 0.5524 5.5%<span class="token punctuation">,</span> 0.5712 8%<span class="token punctuation">,</span> 0.5893 10.5%<span class="token punctuation">,</span> 0.612 13.5%<span class="token punctuation">,</span> 0.6456 17.67%<span class="token punctuation">,</span> 0.6741 21.17%<span class="token punctuation">,</span> 0.7042 25%<span class="token punctuation">,</span> 0.7392 29.83%<span class="token punctuation">,</span> 0.7839 36.83%<span class="token punctuation">,</span> 0.832 45.5%<span class="token punctuation">,</span> 0.8749 54%<span class="token punctuation">,</span> 0.9116 62%<span class="token punctuation">,</span> 0.9429 70%<span class="token punctuation">,</span> 0.9673 77.83%<span class="token punctuation">,</span> 0.9874 86.17%<span class="token punctuation">,</span> 0.9997 94.83%<span class="token punctuation">,</span> 1 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.6066 2.83%<span class="token punctuation">,</span> 0.533 5.5%<span class="token punctuation">,</span> 0.4763 8%<span class="token punctuation">,</span> 0.4293 10.5%<span class="token punctuation">,</span> 0.3839 13.5%<span class="token punctuation">,</span> 0.3381 17.67%<span class="token punctuation">,</span> 0.3124 21.17%<span class="token punctuation">,</span> 0.2948 25%<span class="token punctuation">,</span> 0.2848 29.83%<span class="token punctuation">,</span> 0.2892 36.83%<span class="token punctuation">,</span> 0.3189 45.5%<span class="token punctuation">,</span> 0.3671 54%<span class="token punctuation">,</span> 0.427 62%<span class="token punctuation">,</span> 0.4988 70%<span class="token punctuation">,</span> 0.5779 77.83%<span class="token punctuation">,</span> 0.6689 86.17%<span class="token punctuation">,</span> 0.7685 94.83%<span class="token punctuation">,</span> 0.8291 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #2ecc71<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.5113 2.83%<span class="token punctuation">,</span> 0.5162 5.5%<span class="token punctuation">,</span> 0.5208 8%<span class="token punctuation">,</span> 0.5258 10.5%<span class="token punctuation">,</span> 0.5323 13.5%<span class="token punctuation">,</span> 0.5417 17.67%<span class="token punctuation">,</span> 0.5498 21.17%<span class="token punctuation">,</span> 0.5584 25%<span class="token punctuation">,</span> 0.5688 29.83%<span class="token punctuation">,</span> 0.5825 36.83%<span class="token punctuation">,</span> 0.5971 45.5%<span class="token punctuation">,</span> 0.6088 54%<span class="token punctuation">,</span> 0.6176 62%<span class="token punctuation">,</span> 0.6248 70%<span class="token punctuation">,</span> 0.6307 77.83%<span class="token punctuation">,</span> 0.6368 86.17%<span class="token punctuation">,</span> 0.644 94.83%<span class="token punctuation">,</span> 0.6492 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.confetti-19</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 20.67px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.4964 0%<span class="token punctuation">,</span> 0.5073 2.83%<span class="token punctuation">,</span> 0.5052 5.5%<span class="token punctuation">,</span> 0.4868 8%<span class="token punctuation">,</span> 0.4551 10.5%<span class="token punctuation">,</span> 0.4134 13.5%<span class="token punctuation">,</span> 0.3696 17.67%<span class="token punctuation">,</span> 0.3484 21.17%<span class="token punctuation">,</span> 0.3374 25%<span class="token punctuation">,</span> 0.3342 29.83%<span class="token punctuation">,</span> 0.331 36.83%<span class="token punctuation">,</span> 0.3183 45.5%<span class="token punctuation">,</span> 0.3117 54%<span class="token punctuation">,</span> 0.3381 62%<span class="token punctuation">,</span> 0.3815 70%<span class="token punctuation">,</span> 0.4023 77.83%<span class="token punctuation">,</span> 0.407 86.17%<span class="token punctuation">,</span> 0.4156 94.83%<span class="token punctuation">,</span> 0.4348 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.7047 0%<span class="token punctuation">,</span> 0.6178 2.83%<span class="token punctuation">,</span> 0.5472 5.5%<span class="token punctuation">,</span> 0.4942 8%<span class="token punctuation">,</span> 0.4588 10.5%<span class="token punctuation">,</span> 0.4395 13.5%<span class="token punctuation">,</span> 0.4398 17.67%<span class="token punctuation">,</span> 0.4507 21.17%<span class="token punctuation">,</span> 0.4662 25%<span class="token punctuation">,</span> 0.4848 29.83%<span class="token punctuation">,</span> 0.5093 36.83%<span class="token punctuation">,</span> 0.5533 45.5%<span class="token punctuation">,</span> 0.6184 54%<span class="token punctuation">,</span> 0.6807 62%<span class="token punctuation">,</span> 0.7167 70%<span class="token punctuation">,</span> 0.7399 77.83%<span class="token punctuation">,</span> 0.7814 86.17%<span class="token punctuation">,</span> 0.8465 94.83%<span class="token punctuation">,</span> 0.8871 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token selector">.paper</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #f1c40f<span class="token punctuation">;</span> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> <span class="token function">linear</span><span class="token punctuation">(</span>0.5047 0%<span class="token punctuation">,</span> 0.4985 2.83%<span class="token punctuation">,</span> 0.4862 5.5%<span class="token punctuation">,</span> 0.4691 8%<span class="token punctuation">,</span> 0.4485 10.5%<span class="token punctuation">,</span> 0.4223 13.5%<span class="token punctuation">,</span> 0.3878 17.67%<span class="token punctuation">,</span> 0.3618 21.17%<span class="token punctuation">,</span> 0.3363 25%<span class="token punctuation">,</span> 0.3082 29.83%<span class="token punctuation">,</span> 0.2746 36.83%<span class="token punctuation">,</span> 0.2433 45.5%<span class="token punctuation">,</span> 0.2163 54%<span class="token punctuation">,</span> 0.1862 62%<span class="token punctuation">,</span> 0.1529 70%<span class="token punctuation">,</span> 0.124 77.83%<span class="token punctuation">,</span> 0.1002 86.17%<span class="token punctuation">,</span> 0.0774 94.83%<span class="token punctuation">,</span> 0.0608 100%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre>

2026/8/3
阅读更多

Flow field visualisation on my homepage

<p>I created a new visualisation for my homepage rotation.</p> <p>Here’s a demo:</p> <figure style="display:grid;max-width:300px;aspect-ratio:1;border-radius:50%;overflow:hidden"> <div><canvas id="defaultCanvas0" class="p5Canvas" style="width: 300px; height: 0px;" width="300" height="0"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas><canvas width="128" height="128" style="display: none; width: 128px; height: 128px;"></canvas></div><div class="particles-noise-overlay"></div> <noscript> <img src="screenshot.png"> </noscript> <img data-rss="only" src="https://leanrada.com/notes/flow-field-homepage/screenshot.png?ref=rss"> </figure> <p>It’s just a basic flow field simulation with particles tracing their paths along the space. I used the <a href="https://p5js.org/" target="_blank">p5.js</a> framework. It reacts to mouse movements!</p> <p>I did a few optimisations to make it smoother, but in the end I had to lower the framerate and tried to make the low frame rate look intentional. Optimisation was important especially for mobile devices.</p> <p>Some of the optimisations include:</p> <ul> <li>Using sprites / textures instead of the vector / shape drawing APIs. This was the biggest speedup. This meant I had to predraw the particle (a small dot) on a transparent texture, and use that texture at runtime instead. </li><li>Sharing the same sprites across particles. This was also big one. I think this is called ‘instancing’. </li><li>Having more particles per particle. The actual sprite is a big transparent square that contains many dots. This ‘dot spray’ sprite is stamped for each actual simulated particle. This means we can reduce the amount of simulated particles, or CPU workload, without sacrificing apparent particle density. To hide the effect, the stamped square can be resized dynamically based on the speed variance. </li><li>Using multiple sprites per state. For example, a sprite for each level of opacity. I’m not really sure about this but p5 or something seemed to slow down when having dynamic alpha per particle as opposed to pre-rendered alphas. Maybe something to do with premultiplied alpha? </li><li>Dynamically adjust the amount of simulated particles based on runtime performance. As the simulated particles decrease, the amount of stamped dots per particle increase to compensate. There is a target apparent dot density regardless of performance.</li> </ul> <p>I think moving the whole particle simulation to the GPU would be the real move, but I didn’t want to bother with shaders and WebGL device compatibility for the moment. Maybe later as a learning project. Or maybe I’ll just port this to PixiJS.</p> <p>The flow field is generated by Perlin noise, and particles are accelerated according to the field, with the usual position, velocity, and friction dampening basic physics simulation.</p> <p>This is now part of my home page intro section’s selection of visualizations.</p>

2026/3/22
阅读更多

Naturalest sort

<p><em>For RSS readers: This article contains interactive content available on the <a href="https://leanrada.com/notes/naturalest-sort/?ref=rss">original post on leanrada.com</a>.</em></p> <p>You may have heard of the legendary <a href="http://www.overset.com/2008/09/01/javascript-natural-sort-algorithm/" target="_blank"><code>naturalSort</code></a> in JavaScript.</p> <pre><code>/* * Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license * Author: Jim Palmer (based on chunking idea from Dave Koelle) */ function naturalSort (a, b) { var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi, sre = /(^[ ]*|[ ]*$)/g, dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/, hre = /^0x[0-9a-f]+$/i, ore = /^0/, i = function(s) { return naturalSort.insensitive &amp;&amp; (''+s).toLowerCase() || ''+s }, // convert all to strings strip whitespace x = i(a).replace(sre, '') || '', y = i(b).replace(sre, '') || '', // chunk/tokenize xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), // numeric, hex or date detection xD = parseInt(x.match(hre)) || (xN.length != 1 &amp;&amp; x.match(dre) &amp;&amp; Date.parse(x)), yD = parseInt(y.match(hre)) || xD &amp;&amp; y.match(dre) &amp;&amp; Date.parse(y) || null, oFxNcL, oFyNcL; // first try and sort Hex codes or Dates if (yD) if ( xD &lt; yD ) return -1; else if ( xD &gt; yD ) return 1; // natural sorting through split numeric strings and default strings for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc &lt; numS; cLoc++) { // find floats not starting with '0', string or 0 if not defined (Clint Priest) oFxNcL = !(xN[cLoc] || '').match(ore) &amp;&amp; parseFloat(xN[cLoc]) || xN[cLoc] || 0; oFyNcL = !(yN[cLoc] || '').match(ore) &amp;&amp; parseFloat(yN[cLoc]) || yN[cLoc] || 0; // handle numeric vs string comparison - number &lt; string - (Kyle Adams) if (isNaN(oFxNcL) !== isNaN(oFyNcL)) { return (isNaN(oFxNcL)) ? 1 : -1; } // rely on string comparison if different types - i.e. '02' &lt; 2 != '02' &lt; '2' else if (typeof oFxNcL !== typeof oFyNcL) { oFxNcL += ''; oFyNcL += ''; } if (oFxNcL &lt; oFyNcL) return -1; if (oFxNcL &gt; oFyNcL) return 1; } return 0; }</code></pre> <h2>Natural sort</h2> <p><a href="https://en.wikipedia.org/wiki/Natural_sort_order" target="_blank">Natural sorting</a> aims to order strings based on <a href="https://blog.codinghorror.com/sorting-for-humans-natural-sort-order/" target="_blank">what makes sense for humans</a>. This is in contrast to standard mechanical ordering based on lexicographical order like in physical dictionaries of old.</p> <figure> <img alt="file2.png,file3.png,file6.png,file7.png,file8.png,file27.png,file50.png,file54.png,file68.png,file72.png" src="https://leanrada.com/notes/naturalest-sort/fm-sort.png?ref=rss" width="200" height="300"> <figcaption>Natural sort</figcaption> </figure> <figure> <img alt="file27.png,file2.png,file340.png,file376.png,file3.png,file453.png,file509.png,file50.png,file54.png,file579.png" src="https://leanrada.com/notes/naturalest-sort/unix-sort.png?ref=rss" width="200" height="300"> <figcaption>Lexical sort</figcaption> </figure> <p>But what if we could sort <em>any</em> word without having to program every edge case? What about sequences like <code>low</code>, <code>medium</code>, <code>high</code>? Or <code>heaven</code>, <code>earth</code>, <code>hell</code>?</p> <p>Surely LLMs are the right hammer for this job?</p> <h2>Naturalest sort?</h2> <p>You may have heard of the more recent <a href="https://github.com/abyesilyurt/vibesort/" target="_blank"><code>vibeSort</code></a>.</p> <pre><code>Input: file1,file10,file2 Output: {"sorted":["file1","file2","file10"]} Input: low,high,medium Output: {"sorted":["low","medium","high"]} Input: heaven,hell,earth Output: {"sorted":["heaven","earth","hell"]} Input: monday,friday,tuesday,sunday Output: {"sorted":["monday","tuesday","friday","sunday"]}</code></pre> <p>I think LLMs are overkill for this use case. LLMs may be the most natural sorter, but I was looking for something lighter.</p> <p>Can we do this without LLMs? Let’s look at a less natural natural sort.</p> <h1>Natural sort using word embeddings</h1> <p>The idea behind this is to use <a href="https://en.wikipedia.org/wiki/Word_embedding" target="_blank"><strong>word embeddings</strong></a>, or vectors that represent words in a conceptual space. The question is, are word embeddings sortable? Are embeddings even orderable? Is the order transitive? I set out to experiment.</p> <h2>Quick intro to word embedding arithmetic</h2> <p>Basically, we turn words into numbers so we can do math on them. Embeddings are high-dimensional vectors (&gt; 300 coordinates) that describe word meanings. The key aspect is that words with similar meanings have vectors that are close in this conceptual space.</p> <p>Since they are vectors, you can do math on them. A popular cherry-picked example is to add and subtract embeddings to ‘calculate’ the words <code>King - Man + Woman = Queen</code>.</p> <figure> <img src="https://leanrada.com/notes/naturalest-sort/projector.png?ref=rss" alt="3D projection of word embeddings with man, woman, king, queen points highlighted" width="1186" height="879"> <figcaption>This is the embedding space <a href="https://projector.tensorflow.org/" target="_blank">projected</a> to 3D using principal component analysis, with a few interesting words highlighted.</figcaption> </figure> <p>The main idea is that individual dimensions encode specific meanings or features. In the <code>King - Man + Woman = Queen</code> example, there could be a <code>Gender</code> axis or a <code>Nobility</code> axis that makes the arithmetic work.</p> <figure> <img src="https://leanrada.com/notes/naturalest-sort/gender-nobility.jpg?ref=rss" alt="2D scattterplot showing gender and nobility as dimensions and man, woman, king, queen as points" width="600" height="400"> <figcaption>Man, woman, king, and queen projected to 2D (not-to-scale illustration only). I made up the labels for the two resulting dimensions.</figcaption> </figure> <p><strong>The main hypothesis:</strong> Could there be an <strong><i>order axis</i></strong> in this conceptual space that encodes the natural order of words?</p> <p>Is there an order vector that describes the general direction of going from Monday to Tuesday as well as low to medium to high?</p> <p>If there is, the can we sort words by their embeddings’ positions based on this direction?</p> <h2>The search for the order vector</h2> <p>Naive solution: Just subtract a small-sounding word (say, <code>"smallest"</code>) from a big word (<code>"largest"</code>) and use the difference vector as the direction to order words in.</p> <p> <code>orderVec = word2vec("largest") - word2vec("smallest")</code> </p> <p>Projecting the input words onto this order vector should in theory give us a straightforward linear value for each word which is trivially sortable. <small>Btw I’m using precomputed word2vec from Google to get word embeddings.</small></p> <pre><code> // Compute the order direction vector const orderDir = normalize(word2vec("largest") - word2vec("smallest")); // Get the embeddings of the input words const vecs = words.map(word2vec); const ranks = Object.fromEntries(vecs.map((vec, i) =&gt; // Project each word by calculating // its dot product against the 'order vector' [words[i], dot(vec, orderDir)] )); // Sort words according to the order vector ranking! words.sort((a, b) =&gt; ranks[a] - ranks[b]);</code></pre> <p>Does it work? Let’s visualise an example:</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 138"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> "smallest" to "largest" </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="48.336414804538094" y1="48" x2="48.336414804538094" y2="54" stroke-width="1"></line> , <line x1="67.20721288942408" y1="48" x2="67.20721288942408" y2="54" stroke-width="1"></line> , <line x1="86.07801097431008" y1="48" x2="86.07801097431008" y2="54" stroke-width="1"></line> , <line x1="104.94880905919605" y1="48" x2="104.94880905919605" y2="54" stroke-width="1"></line> , <line x1="123.81960714408201" y1="48" x2="123.81960714408201" y2="54" stroke-width="1"></line> , <line x1="142.69040522896796" y1="48" x2="142.69040522896796" y2="54" stroke-width="1"></line> , <line x1="161.56120331385395" y1="48" x2="161.56120331385395" y2="54" stroke-width="1"></line> , <line x1="180.4320013987399" y1="48" x2="180.4320013987399" y2="54" stroke-width="1"></line> , <line x1="199.30279948362588" y1="48" x2="199.30279948362588" y2="54" stroke-width="1"></line> , <line x1="218.17359756851184" y1="48" x2="218.17359756851184" y2="54" stroke-width="1"></line> , <line x1="237.04439565339783" y1="48" x2="237.04439565339783" y2="54" stroke-width="1"></line> , <line x1="255.9151937382838" y1="48" x2="255.9151937382838" y2="54" stroke-width="1"></line> , <line x1="274.78599182316975" y1="48" x2="274.78599182316975" y2="54" stroke-width="1"></line> , <line x1="293.6567899080557" y1="48" x2="293.6567899080557" y2="54" stroke-width="1"></line> , <line x1="312.52758799294173" y1="48" x2="312.52758799294173" y2="54" stroke-width="1"></line> , <line x1="331.39838607782764" y1="48" x2="331.39838607782764" y2="54" stroke-width="1"></line> , <line x1="350.26918416271366" y1="48" x2="350.26918416271366" y2="54" stroke-width="1"></line> , <line x1="369.1399822475996" y1="48" x2="369.1399822475996" y2="54" stroke-width="1"></line> <line x1="350.26918416271366" y1="48" x2="350.26918416271366" y2="54" stroke-width="1.5"></line> <text x="350.26918416271366" y="45" text-anchor="middle">0</text> <g> <circle cx="55.66666666666667" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666667" cy="51" r="3"></circle> <text x="55.66666666666667" y="44" text-anchor="middle">-0.31</text> <text x="52.66666666666667" y="84" text-anchor="end">tiny</text> <line x1="55.66666666666667" y1="54" x2="55.66666666666667" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="103.38621334216563" cy="51" r="7" fill="transparent"></circle> <circle cx="103.38621334216563" cy="51" r="3"></circle> <text x="103.38621334216563" y="44" text-anchor="middle">-0.26</text> <text x="100.38621334216563" y="96" text-anchor="end">small</text> <line x1="103.38621334216563" y1="54" x2="103.38621334216563" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="310.10371948727743" cy="51" r="7" fill="transparent"></circle> <circle cx="310.10371948727743" cy="51" r="3"></circle> <text x="310.10371948727743" y="44" text-anchor="middle">-0.04</text> <text x="307.10371948727743" y="108" text-anchor="end">medium</text> <line x1="310.10371948727743" y1="54" x2="310.10371948727743" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="322.5428103235072" cy="51" r="7" fill="transparent"></circle> <circle cx="322.5428103235072" cy="51" r="3"></circle> <text x="322.5428103235072" y="44" text-anchor="middle">-0.03</text> <text x="319.5428103235072" y="120" text-anchor="end">large</text> <line x1="322.5428103235072" y1="54" x2="322.5428103235072" y2="120" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.02</text> <text x="369.3333333333333" y="132" text-anchor="end">huge</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="132" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>tiny,<wbr>small,<wbr>medium,<wbr>large,<wbr>huge</code> </td></tr></tbody></table> </figure> <p>The above diagram shows the word embedding vectors for tiny, small, etc., collapsed onto a single line — the line from <code>"smallest"</code> to <code>"largest"</code>.</p> <p><strong>Sorting words by the scalar values of their projections on the smallest-to-largest direction seems to produce a ‘natural’ sort!</strong></p> <p>It can even sort different kinds of semantic magnitudes, not just sizes per se, a couple of examples:</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 126"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> "smallest" to "largest" </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="51.085478407202785" y1="48" x2="51.085478407202785" y2="54" stroke-width="1"></line> , <line x1="70.61561421695694" y1="48" x2="70.61561421695694" y2="54" stroke-width="1"></line> , <line x1="90.14575002671108" y1="48" x2="90.14575002671108" y2="54" stroke-width="1"></line> , <line x1="109.67588583646524" y1="48" x2="109.67588583646524" y2="54" stroke-width="1"></line> , <line x1="129.2060216462194" y1="48" x2="129.2060216462194" y2="54" stroke-width="1"></line> , <line x1="148.73615745597354" y1="48" x2="148.73615745597354" y2="54" stroke-width="1"></line> , <line x1="168.2662932657277" y1="48" x2="168.2662932657277" y2="54" stroke-width="1"></line> , <line x1="187.79642907548185" y1="48" x2="187.79642907548185" y2="54" stroke-width="1"></line> , <line x1="207.326564885236" y1="48" x2="207.326564885236" y2="54" stroke-width="1"></line> , <line x1="226.85670069499014" y1="48" x2="226.85670069499014" y2="54" stroke-width="1"></line> , <line x1="246.3868365047443" y1="48" x2="246.3868365047443" y2="54" stroke-width="1"></line> , <line x1="265.9169723144985" y1="48" x2="265.9169723144985" y2="54" stroke-width="1"></line> , <line x1="285.4471081242526" y1="48" x2="285.4471081242526" y2="54" stroke-width="1"></line> , <line x1="304.97724393400676" y1="48" x2="304.97724393400676" y2="54" stroke-width="1"></line> , <line x1="324.5073797437609" y1="48" x2="324.5073797437609" y2="54" stroke-width="1"></line> , <line x1="344.037515553515" y1="48" x2="344.037515553515" y2="54" stroke-width="1"></line> , <line x1="363.56765136326914" y1="48" x2="363.56765136326914" y2="54" stroke-width="1"></line> , <line x1="383.09778717302333" y1="48" x2="383.09778717302333" y2="54" stroke-width="1"></line> <line x1="148.73615745597354" y1="48" x2="148.73615745597354" y2="54" stroke-width="1.5"></line> <text x="148.73615745597354" y="45" text-anchor="middle">0</text> <g> <circle cx="55.66666666666667" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666667" cy="51" r="3"></circle> <text x="55.66666666666667" y="44" text-anchor="middle">-0.02</text> <text x="52.66666666666667" y="84" text-anchor="end">breeze</text> <line x1="55.66666666666667" y1="54" x2="55.66666666666667" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="156.8190524659094" cy="51" r="7" fill="transparent"></circle> <circle cx="156.8190524659094" cy="51" r="3"></circle> <text x="156.8190524659094" y="44" text-anchor="middle">0.00</text> <text x="153.8190524659094" y="96" text-anchor="end">wind</text> <line x1="156.8190524659094" y1="54" x2="156.8190524659094" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="358.85622972301525" cy="51" r="7" fill="transparent"></circle> <circle cx="358.85622972301525" cy="51" r="3"></circle> <text x="358.85622972301525" y="44" text-anchor="middle">0.05</text> <text x="355.85622972301525" y="108" text-anchor="end">gale</text> <line x1="358.85622972301525" y1="54" x2="358.85622972301525" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="372.33333333333337" cy="51" r="7" fill="transparent"></circle> <circle cx="372.33333333333337" cy="51" r="3"></circle> <text x="372.33333333333337" y="44" text-anchor="middle">0.06</text> <text x="369.33333333333337" y="120" text-anchor="end">storm</text> <line x1="372.33333333333337" y1="54" x2="372.33333333333337" y2="120" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>breeze,<wbr>wind,<wbr>gale,<wbr>storm</code> </td></tr></tbody></table> </figure> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 114"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> "smallest" to "largest" </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="57.40653652678612" y1="48" x2="57.40653652678612" y2="54" stroke-width="1"></line> , <line x1="71.89787115186459" y1="48" x2="71.89787115186459" y2="54" stroke-width="1"></line> , <line x1="86.38920577694304" y1="48" x2="86.38920577694304" y2="54" stroke-width="1"></line> , <line x1="100.88054040202151" y1="48" x2="100.88054040202151" y2="54" stroke-width="1"></line> , <line x1="115.37187502709997" y1="48" x2="115.37187502709997" y2="54" stroke-width="1"></line> , <line x1="129.86320965217845" y1="48" x2="129.86320965217845" y2="54" stroke-width="1"></line> , <line x1="144.35454427725693" y1="48" x2="144.35454427725693" y2="54" stroke-width="1"></line> , <line x1="158.8458789023354" y1="48" x2="158.8458789023354" y2="54" stroke-width="1"></line> , <line x1="173.33721352741384" y1="48" x2="173.33721352741384" y2="54" stroke-width="1"></line> , <line x1="187.82854815249232" y1="48" x2="187.82854815249232" y2="54" stroke-width="1"></line> , <line x1="202.31988277757077" y1="48" x2="202.31988277757077" y2="54" stroke-width="1"></line> , <line x1="216.81121740264925" y1="48" x2="216.81121740264925" y2="54" stroke-width="1"></line> , <line x1="231.3025520277277" y1="48" x2="231.3025520277277" y2="54" stroke-width="1"></line> , <line x1="245.79388665280618" y1="48" x2="245.79388665280618" y2="54" stroke-width="1"></line> , <line x1="260.2852212778846" y1="48" x2="260.2852212778846" y2="54" stroke-width="1"></line> , <line x1="274.7765559029631" y1="48" x2="274.7765559029631" y2="54" stroke-width="1"></line> , <line x1="289.26789052804156" y1="48" x2="289.26789052804156" y2="54" stroke-width="1"></line> , <line x1="303.75922515312" y1="48" x2="303.75922515312" y2="54" stroke-width="1"></line> , <line x1="318.2505597781984" y1="48" x2="318.2505597781984" y2="54" stroke-width="1"></line> , <line x1="332.7418944032769" y1="48" x2="332.7418944032769" y2="54" stroke-width="1"></line> , <line x1="347.23322902835537" y1="48" x2="347.23322902835537" y2="54" stroke-width="1"></line> , <line x1="361.7245636534338" y1="48" x2="361.7245636534338" y2="54" stroke-width="1"></line> , <line x1="376.2158982785123" y1="48" x2="376.2158982785123" y2="54" stroke-width="1"></line> <line x1="173.33721352741384" y1="48" x2="173.33721352741384" y2="54" stroke-width="1.5"></line> <text x="173.33721352741384" y="45" text-anchor="middle">0</text> <g> <circle cx="55.66666666666666" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666666" cy="51" r="3"></circle> <text x="55.66666666666666" y="44" text-anchor="middle">-0.04</text> <text x="52.66666666666666" y="84" text-anchor="end">poor</text> <line x1="55.66666666666666" y1="54" x2="55.66666666666666" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="201.45048948463088" cy="51" r="7" fill="transparent"></circle> <circle cx="201.45048948463088" cy="51" r="3"></circle> <text x="201.45048948463088" y="44" text-anchor="middle">0.01</text> <text x="198.45048948463088" y="96" text-anchor="end">wealthy</text> <line x1="201.45048948463088" y1="54" x2="201.45048948463088" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="372.33333333333326" cy="51" r="7" fill="transparent"></circle> <circle cx="372.33333333333326" cy="51" r="3"></circle> <text x="372.33333333333326" y="44" text-anchor="middle">0.07</text> <text x="369.33333333333326" y="108" text-anchor="end">rich</text> <line x1="372.33333333333326" y1="54" x2="372.33333333333326" y2="108" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>poor,<wbr>wealthy,<wbr>rich</code> </td></tr></tbody></table> </figure> <p>It starts breaking down as you stray away from size or magnitude:</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 138"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> "smallest" to "largest" </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="48.8759773654119" y1="48" x2="48.8759773654119" y2="54" stroke-width="1"></line> , <line x1="67.57147070448454" y1="48" x2="67.57147070448454" y2="54" stroke-width="1"></line> , <line x1="86.2669640435572" y1="48" x2="86.2669640435572" y2="54" stroke-width="1"></line> , <line x1="104.96245738262984" y1="48" x2="104.96245738262984" y2="54" stroke-width="1"></line> , <line x1="123.65795072170248" y1="48" x2="123.65795072170248" y2="54" stroke-width="1"></line> , <line x1="142.35344406077513" y1="48" x2="142.35344406077513" y2="54" stroke-width="1"></line> , <line x1="161.04893739984777" y1="48" x2="161.04893739984777" y2="54" stroke-width="1"></line> , <line x1="179.7444307389204" y1="48" x2="179.7444307389204" y2="54" stroke-width="1"></line> , <line x1="198.43992407799308" y1="48" x2="198.43992407799308" y2="54" stroke-width="1"></line> , <line x1="217.1354174170657" y1="48" x2="217.1354174170657" y2="54" stroke-width="1"></line> , <line x1="235.83091075613834" y1="48" x2="235.83091075613834" y2="54" stroke-width="1"></line> , <line x1="254.52640409521098" y1="48" x2="254.52640409521098" y2="54" stroke-width="1"></line> , <line x1="273.2218974342836" y1="48" x2="273.2218974342836" y2="54" stroke-width="1"></line> , <line x1="291.9173907733563" y1="48" x2="291.9173907733563" y2="54" stroke-width="1"></line> , <line x1="310.6128841124289" y1="48" x2="310.6128841124289" y2="54" stroke-width="1"></line> , <line x1="329.3083774515015" y1="48" x2="329.3083774515015" y2="54" stroke-width="1"></line> , <line x1="348.0038707905742" y1="48" x2="348.0038707905742" y2="54" stroke-width="1"></line> , <line x1="366.6993641296469" y1="48" x2="366.6993641296469" y2="54" stroke-width="1"></line> <line x1="179.7444307389204" y1="48" x2="179.7444307389204" y2="54" stroke-width="1.5"></line> <text x="179.7444307389204" y="45" text-anchor="middle">0</text> <g> <circle cx="55.66666666666667" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666667" cy="51" r="3"></circle> <text x="55.66666666666667" y="44" text-anchor="middle">-0.03</text> <text x="52.66666666666667" y="84" text-anchor="end">first</text> <line x1="55.66666666666667" y1="54" x2="55.66666666666667" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="158.55998841409001" cy="51" r="7" fill="transparent"></circle> <circle cx="158.55998841409001" cy="51" r="3"></circle> <text x="158.55998841409001" y="44" text-anchor="middle">-0.01</text> <text x="155.55998841409001" y="96" text-anchor="end">fourth</text> <line x1="158.55998841409001" y1="54" x2="158.55998841409001" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="192.67013956827074" cy="51" r="7" fill="transparent"></circle> <circle cx="192.67013956827074" cy="51" r="3"></circle> <text x="192.67013956827074" y="44" text-anchor="middle">0.00</text> <text x="189.67013956827074" y="108" text-anchor="end">second</text> <line x1="192.67013956827074" y1="54" x2="192.67013956827074" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="279.3072403326832" cy="51" r="7" fill="transparent"></circle> <circle cx="279.3072403326832" cy="51" r="3"></circle> <text x="279.3072403326832" y="44" text-anchor="middle">0.03</text> <text x="276.3072403326832" y="120" text-anchor="end">fifth</text> <line x1="279.3072403326832" y1="54" x2="279.3072403326832" y2="120" stroke-width="1"></line> </g>,<g> <circle cx="372.33333333333337" cy="51" r="7" fill="transparent"></circle> <circle cx="372.33333333333337" cy="51" r="3"></circle> <text x="372.33333333333337" y="44" text-anchor="middle">0.05</text> <text x="369.33333333333337" y="132" text-anchor="end">third</text> <line x1="372.33333333333337" y1="54" x2="372.33333333333337" y2="132" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>first,<wbr>fourth,<wbr>second,<wbr>fifth,<wbr>third</code> </td></tr></tbody></table> </figure> <p>When sorting words completely unrelated to the concept of size/magnitude, like love and hate, it fails.</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 126"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> "smallest" to "largest" </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="52.82735047096426" y1="48" x2="52.82735047096426" y2="54" stroke-width="1"></line> , <line x1="70.4584062947194" y1="48" x2="70.4584062947194" y2="54" stroke-width="1"></line> , <line x1="88.08946211847451" y1="48" x2="88.08946211847451" y2="54" stroke-width="1"></line> , <line x1="105.72051794222963" y1="48" x2="105.72051794222963" y2="54" stroke-width="1"></line> , <line x1="123.35157376598477" y1="48" x2="123.35157376598477" y2="54" stroke-width="1"></line> , <line x1="140.98262958973987" y1="48" x2="140.98262958973987" y2="54" stroke-width="1"></line> , <line x1="158.61368541349498" y1="48" x2="158.61368541349498" y2="54" stroke-width="1"></line> , <line x1="176.24474123725008" y1="48" x2="176.24474123725008" y2="54" stroke-width="1"></line> , <line x1="193.87579706100516" y1="48" x2="193.87579706100516" y2="54" stroke-width="1"></line> , <line x1="211.50685288476026" y1="48" x2="211.50685288476026" y2="54" stroke-width="1"></line> , <line x1="229.1379087085154" y1="48" x2="229.1379087085154" y2="54" stroke-width="1"></line> , <line x1="246.76896453227053" y1="48" x2="246.76896453227053" y2="54" stroke-width="1"></line> , <line x1="264.4000203560256" y1="48" x2="264.4000203560256" y2="54" stroke-width="1"></line> , <line x1="282.0310761797807" y1="48" x2="282.0310761797807" y2="54" stroke-width="1"></line> , <line x1="299.6621320035359" y1="48" x2="299.6621320035359" y2="54" stroke-width="1"></line> , <line x1="317.293187827291" y1="48" x2="317.293187827291" y2="54" stroke-width="1"></line> , <line x1="334.9242436510461" y1="48" x2="334.9242436510461" y2="54" stroke-width="1"></line> , <line x1="352.5552994748012" y1="48" x2="352.5552994748012" y2="54" stroke-width="1"></line> , <line x1="370.1863552985563" y1="48" x2="370.1863552985563" y2="54" stroke-width="1"></line> <line x1="352.5552994748012" y1="48" x2="352.5552994748012" y2="54" stroke-width="1.5"></line> <text x="352.5552994748012" y="45" text-anchor="middle">0</text> <g> <circle cx="55.666666666666664" cy="51" r="7" fill="transparent"></circle> <circle cx="55.666666666666664" cy="51" r="3"></circle> <text x="55.666666666666664" y="44" text-anchor="middle">-0.08</text> <text x="52.666666666666664" y="84" text-anchor="end">like</text> <line x1="55.666666666666664" y1="54" x2="55.666666666666664" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="136.7930493153529" cy="51" r="7" fill="transparent"></circle> <circle cx="136.7930493153529" cy="51" r="3"></circle> <text x="136.7930493153529" y="44" text-anchor="middle">-0.06</text> <text x="133.7930493153529" y="96" text-anchor="end">love</text> <line x1="136.7930493153529" y1="54" x2="136.7930493153529" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="208.77668756441432" cy="51" r="7" fill="transparent"></circle> <circle cx="208.77668756441432" cy="51" r="3"></circle> <text x="208.77668756441432" y="44" text-anchor="middle">-0.04</text> <text x="205.77668756441432" y="108" text-anchor="end">dislike</text> <line x1="208.77668756441432" y1="54" x2="208.77668756441432" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.01</text> <text x="369.3333333333333" y="120" text-anchor="end">hate</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="120" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>like,<wbr>love,<wbr>dislike,<wbr>hate</code> </td></tr></tbody></table> </figure> <p>The above example fails because our chosen anchor words <code>"smallest"</code> and <code>"largest"</code> have nothing to do with the love-hate scale. Well, I guess it’s still being sorted in a way, maybe from the least intense to the most intense emotion? so there’s that.</p> <h2>Other anchor words</h2> <p>The examples thus far have been using <code>"smallest"</code> and <code>"largest"</code> as ‘anchor words’.</p> <p>What if we sort by different anchor pairs? How about <code>"negative"</code> to <code>"positive"</code>?</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 126"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> "negative" to "positive" </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="43.666171470395696" y1="48" x2="43.666171470395696" y2="54" stroke-width="1"></line> , <line x1="59.78366470489938" y1="48" x2="59.78366470489938" y2="54" stroke-width="1"></line> , <line x1="75.90115793940308" y1="48" x2="75.90115793940308" y2="54" stroke-width="1"></line> , <line x1="92.01865117390675" y1="48" x2="92.01865117390675" y2="54" stroke-width="1"></line> , <line x1="108.1361444084104" y1="48" x2="108.1361444084104" y2="54" stroke-width="1"></line> , <line x1="124.25363764291407" y1="48" x2="124.25363764291407" y2="54" stroke-width="1"></line> , <line x1="140.37113087741773" y1="48" x2="140.37113087741773" y2="54" stroke-width="1"></line> , <line x1="156.4886241119214" y1="48" x2="156.4886241119214" y2="54" stroke-width="1"></line> , <line x1="172.60611734642507" y1="48" x2="172.60611734642507" y2="54" stroke-width="1"></line> , <line x1="188.72361058092872" y1="48" x2="188.72361058092872" y2="54" stroke-width="1"></line> , <line x1="204.84110381543243" y1="48" x2="204.84110381543243" y2="54" stroke-width="1"></line> , <line x1="220.95859704993606" y1="48" x2="220.95859704993606" y2="54" stroke-width="1"></line> , <line x1="237.07609028443974" y1="48" x2="237.07609028443974" y2="54" stroke-width="1"></line> , <line x1="253.1935835189434" y1="48" x2="253.1935835189434" y2="54" stroke-width="1"></line> , <line x1="269.3110767534471" y1="48" x2="269.3110767534471" y2="54" stroke-width="1"></line> , <line x1="285.42856998795077" y1="48" x2="285.42856998795077" y2="54" stroke-width="1"></line> , <line x1="301.5460632224544" y1="48" x2="301.5460632224544" y2="54" stroke-width="1"></line> , <line x1="317.66355645695813" y1="48" x2="317.66355645695813" y2="54" stroke-width="1"></line> , <line x1="333.78104969146176" y1="48" x2="333.78104969146176" y2="54" stroke-width="1"></line> , <line x1="349.89854292596544" y1="48" x2="349.89854292596544" y2="54" stroke-width="1"></line> , <line x1="366.0160361604691" y1="48" x2="366.0160361604691" y2="54" stroke-width="1"></line> , <line x1="382.1335293949728" y1="48" x2="382.1335293949728" y2="54" stroke-width="1"></line> <line x1="301.5460632224544" y1="48" x2="301.5460632224544" y2="54" stroke-width="1.5"></line> <text x="301.5460632224544" y="45" text-anchor="middle">0</text> <g> <circle cx="55.66666666666667" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666667" cy="51" r="3"></circle> <text x="55.66666666666667" y="44" text-anchor="middle">-0.15</text> <text x="52.66666666666667" y="84" text-anchor="end">hate</text> <line x1="55.66666666666667" y1="54" x2="55.66666666666667" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="108.97132757399092" cy="51" r="7" fill="transparent"></circle> <circle cx="108.97132757399092" cy="51" r="3"></circle> <text x="108.97132757399092" y="44" text-anchor="middle">-0.12</text> <text x="105.97132757399092" y="96" text-anchor="end">dislike</text> <line x1="108.97132757399092" y1="54" x2="108.97132757399092" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="358.50964664364676" cy="51" r="7" fill="transparent"></circle> <circle cx="358.50964664364676" cy="51" r="3"></circle> <text x="358.50964664364676" y="44" text-anchor="middle">0.04</text> <text x="355.50964664364676" y="108" text-anchor="end">love</text> <line x1="358.50964664364676" y1="54" x2="358.50964664364676" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="372.33333333333337" cy="51" r="7" fill="transparent"></circle> <circle cx="372.33333333333337" cy="51" r="3"></circle> <text x="372.33333333333337" y="44" text-anchor="middle">0.04</text> <text x="369.33333333333337" y="120" text-anchor="end">like</text> <line x1="372.33333333333337" y1="54" x2="372.33333333333337" y2="120" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>hate,<wbr>dislike,<wbr>love,<wbr>like</code> </td></tr></tbody></table> </figure> <p>Nice, it almost sorts the love-hate example.</p> <p>How about <code>"good"</code> and <code>"bad"</code>?</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 126"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> "bad" to "good" </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="51.89310288233331" y1="48" x2="51.89310288233331" y2="54" stroke-width="1"></line> , <line x1="75.27463775941783" y1="48" x2="75.27463775941783" y2="54" stroke-width="1"></line> , <line x1="98.65617263650238" y1="48" x2="98.65617263650238" y2="54" stroke-width="1"></line> , <line x1="122.03770751358691" y1="48" x2="122.03770751358691" y2="54" stroke-width="1"></line> , <line x1="145.41924239067146" y1="48" x2="145.41924239067146" y2="54" stroke-width="1"></line> , <line x1="168.80077726775596" y1="48" x2="168.80077726775596" y2="54" stroke-width="1"></line> , <line x1="192.18231214484052" y1="48" x2="192.18231214484052" y2="54" stroke-width="1"></line> , <line x1="215.56384702192503" y1="48" x2="215.56384702192503" y2="54" stroke-width="1"></line> , <line x1="238.9453818990096" y1="48" x2="238.9453818990096" y2="54" stroke-width="1"></line> , <line x1="262.3269167760941" y1="48" x2="262.3269167760941" y2="54" stroke-width="1"></line> , <line x1="285.7084516531786" y1="48" x2="285.7084516531786" y2="54" stroke-width="1"></line> , <line x1="309.08998653026316" y1="48" x2="309.08998653026316" y2="54" stroke-width="1"></line> , <line x1="332.4715214073477" y1="48" x2="332.4715214073477" y2="54" stroke-width="1"></line> , <line x1="355.8530562844323" y1="48" x2="355.8530562844323" y2="54" stroke-width="1"></line> , <line x1="379.2345911615168" y1="48" x2="379.2345911615168" y2="54" stroke-width="1"></line> <line x1="215.56384702192503" y1="48" x2="215.56384702192503" y2="54" stroke-width="1.5"></line> <text x="215.56384702192503" y="45" text-anchor="middle">0</text> <g> <circle cx="55.66666666666667" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666667" cy="51" r="3"></circle> <text x="55.66666666666667" y="44" text-anchor="middle">-0.14</text> <text x="52.66666666666667" y="84" text-anchor="end">hate</text> <line x1="55.66666666666667" y1="54" x2="55.66666666666667" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="145.98879101002504" cy="51" r="7" fill="transparent"></circle> <circle cx="145.98879101002504" cy="51" r="3"></circle> <text x="145.98879101002504" y="44" text-anchor="middle">-0.06</text> <text x="142.98879101002504" y="96" text-anchor="end">dislike</text> <line x1="145.98879101002504" y1="54" x2="145.98879101002504" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="354.78919198480503" cy="51" r="7" fill="transparent"></circle> <circle cx="354.78919198480503" cy="51" r="3"></circle> <text x="354.78919198480503" y="44" text-anchor="middle">0.12</text> <text x="351.78919198480503" y="108" text-anchor="end">love</text> <line x1="354.78919198480503" y1="54" x2="354.78919198480503" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.13</text> <text x="369.3333333333333" y="120" text-anchor="end">like</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="120" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>hate,<wbr>dislike,<wbr>love,<wbr>like</code> </td></tr></tbody></table> </figure> <p>An order vector that can actually sort hate and love is <code>"worst"</code> to <code>"best"</code> for some reason.</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 126"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> "worst" to "best" </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="46.34648825897861" y1="48" x2="46.34648825897861" y2="54" stroke-width="1"></line> , <line x1="70.89011045434887" y1="48" x2="70.89011045434887" y2="54" stroke-width="1"></line> , <line x1="95.43373264971913" y1="48" x2="95.43373264971913" y2="54" stroke-width="1"></line> , <line x1="119.97735484508938" y1="48" x2="119.97735484508938" y2="54" stroke-width="1"></line> , <line x1="144.52097704045963" y1="48" x2="144.52097704045963" y2="54" stroke-width="1"></line> , <line x1="169.0645992358299" y1="48" x2="169.0645992358299" y2="54" stroke-width="1"></line> , <line x1="193.60822143120015" y1="48" x2="193.60822143120015" y2="54" stroke-width="1"></line> , <line x1="218.15184362657038" y1="48" x2="218.15184362657038" y2="54" stroke-width="1"></line> , <line x1="242.69546582194064" y1="48" x2="242.69546582194064" y2="54" stroke-width="1"></line> , <line x1="267.2390880173109" y1="48" x2="267.2390880173109" y2="54" stroke-width="1"></line> , <line x1="291.7827102126812" y1="48" x2="291.7827102126812" y2="54" stroke-width="1"></line> , <line x1="316.3263324080514" y1="48" x2="316.3263324080514" y2="54" stroke-width="1"></line> , <line x1="340.86995460342166" y1="48" x2="340.86995460342166" y2="54" stroke-width="1"></line> , <line x1="365.4135767987919" y1="48" x2="365.4135767987919" y2="54" stroke-width="1"></line> <line x1="144.52097704045963" y1="48" x2="144.52097704045963" y2="54" stroke-width="1.5"></line> <text x="144.52097704045963" y="45" text-anchor="middle">0</text> <g> <circle cx="55.66666666666666" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666666" cy="51" r="3"></circle> <text x="55.66666666666666" y="44" text-anchor="middle">-0.07</text> <text x="52.66666666666666" y="84" text-anchor="end">hate</text> <line x1="55.66666666666666" y1="54" x2="55.66666666666666" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="189.3539241930155" cy="51" r="7" fill="transparent"></circle> <circle cx="189.3539241930155" cy="51" r="3"></circle> <text x="189.3539241930155" y="44" text-anchor="middle">0.04</text> <text x="186.3539241930155" y="96" text-anchor="end">dislike</text> <line x1="189.3539241930155" y1="54" x2="189.3539241930155" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="261.4272671846919" cy="51" r="7" fill="transparent"></circle> <circle cx="261.4272671846919" cy="51" r="3"></circle> <text x="261.4272671846919" y="44" text-anchor="middle">0.10</text> <text x="258.4272671846919" y="108" text-anchor="end">like</text> <line x1="261.4272671846919" y1="54" x2="261.4272671846919" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.19</text> <text x="369.3333333333333" y="120" text-anchor="end">love</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="120" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>hate,<wbr>dislike,<wbr>like,<wbr>love</code> </td></tr></tbody></table> </figure> <p>And of course, the two extremes within the list themselves would work perfectly, but obviously the sorting algorithm wouldn’t know which elements are the extremes beforehand.</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 126"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> "hate" to "love" </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="54.60174364042645" y1="48" x2="54.60174364042645" y2="54" stroke-width="1"></line> , <line x1="72.31269843478381" y1="48" x2="72.31269843478381" y2="54" stroke-width="1"></line> , <line x1="90.02365322914117" y1="48" x2="90.02365322914117" y2="54" stroke-width="1"></line> , <line x1="107.73460802349855" y1="48" x2="107.73460802349855" y2="54" stroke-width="1"></line> , <line x1="125.44556281785592" y1="48" x2="125.44556281785592" y2="54" stroke-width="1"></line> , <line x1="143.15651761221326" y1="48" x2="143.15651761221326" y2="54" stroke-width="1"></line> , <line x1="160.86747240657064" y1="48" x2="160.86747240657064" y2="54" stroke-width="1"></line> , <line x1="178.57842720092802" y1="48" x2="178.57842720092802" y2="54" stroke-width="1"></line> , <line x1="196.2893819952854" y1="48" x2="196.2893819952854" y2="54" stroke-width="1"></line> , <line x1="214.00033678964274" y1="48" x2="214.00033678964274" y2="54" stroke-width="1"></line> , <line x1="231.7112915840001" y1="48" x2="231.7112915840001" y2="54" stroke-width="1"></line> , <line x1="249.4222463783575" y1="48" x2="249.4222463783575" y2="54" stroke-width="1"></line> , <line x1="267.13320117271485" y1="48" x2="267.13320117271485" y2="54" stroke-width="1"></line> , <line x1="284.84415596707225" y1="48" x2="284.84415596707225" y2="54" stroke-width="1"></line> , <line x1="302.5551107614296" y1="48" x2="302.5551107614296" y2="54" stroke-width="1"></line> , <line x1="320.26606555578695" y1="48" x2="320.26606555578695" y2="54" stroke-width="1"></line> , <line x1="337.97702035014436" y1="48" x2="337.97702035014436" y2="54" stroke-width="1"></line> , <line x1="355.6879751445017" y1="48" x2="355.6879751445017" y2="54" stroke-width="1"></line> , <line x1="373.39892993885906" y1="48" x2="373.39892993885906" y2="54" stroke-width="1"></line> <line x1="214.00033678964274" y1="48" x2="214.00033678964274" y2="54" stroke-width="1.5"></line> <text x="214.00033678964274" y="45" text-anchor="middle">0</text> <g> <circle cx="55.66666666666666" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666666" cy="51" r="3"></circle> <text x="55.66666666666666" y="44" text-anchor="middle">-0.45</text> <text x="52.66666666666666" y="84" text-anchor="end">hate</text> <line x1="55.66666666666666" y1="54" x2="55.66666666666666" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="171.24292641968216" cy="51" r="7" fill="transparent"></circle> <circle cx="171.24292641968216" cy="51" r="3"></circle> <text x="171.24292641968216" y="44" text-anchor="middle">-0.12</text> <text x="168.24292641968216" y="96" text-anchor="end">dislike</text> <line x1="171.24292641968216" y1="54" x2="171.24292641968216" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="205.6724913681546" cy="51" r="7" fill="transparent"></circle> <circle cx="205.6724913681546" cy="51" r="3"></circle> <text x="205.6724913681546" y="44" text-anchor="middle">-0.02</text> <text x="202.6724913681546" y="108" text-anchor="end">like</text> <line x1="205.6724913681546" y1="54" x2="205.6724913681546" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.45</text> <text x="369.3333333333333" y="120" text-anchor="end">love</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="120" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>hate,<wbr>dislike,<wbr>like,<wbr>love</code> </td></tr></tbody></table> </figure> <p>The lesson here is that there are many potential order vectors, many axes to sort words in. One may make sense for a given set of input words, and another for a different set. We need to choose the right order direction vector per set of words. But how do we choose, and from what order vector choices?</p> <h2>The search for multiple order vectors</h2> <p>With the help of an LLM, I generated a bunch of anchor pairs for the algorithm to choose order vectors from.</p> <pre><code>const anchors = [ ["least", "most"], ["low", "high"], ["light", "heavy"], ["worst", "best"], ["empty", "full"], ["uncertain", "certain"], ["quiet", "noisy"], ["back", "forward"], ["unusual", "typical"], ["dull", "sharp"], ["tiny", "huge"], ["frozen", "boiling"], ["poverty", "wealth"], ["left", "right"], ["small", "big"], ["early", "late"], ["young", "old"], ["start", "finish"], … ];</code></pre> <p>After removing some hallucinations, I clustered them to reduce the amount of choices to process.</p> <p>Clustering the order vectors seemed to partially validate the hypothesis, because there were two major clusters: one direction seemingly related to increasing magnitude, and the other direction like a general ‘value’ (?) direction. Does this mean there could actually be general order vectors in the conceptual space?</p> <pre><code>CLUSTER SIZE MEMBERS 'value' [61] narrow→wide, closed→open, down→up, low→high, lowest→highest, below→above, weak→strong, weakest→strongest, inferior→superior, sluggish→rapid, dim→bright, worthless→valuable, boring→exciting, … 'magnitude' [40] least→most, few→many, back→forward, off→on, calm→violent, quiet→noisy, dry→wet, rural→urban, basic→advanced, plain→elaborate, cheap→expensive, free→costly, easy→difficult, simple→complex, trivial→challenging, smallest→largest, … 'sequence' [12] first→last, young→old, new→ancient, fresh→stale, beginning→end, initial→final, start→finish, minority→majority, inner→outer, cool→warm, … 'frequency' [10] before→after, none→all, never→always, seldom→often, opening→closing, rare→common, uncertain→certain, … 'size' [9] minimum→maximum, affordable→luxurious, … 'space' [5] particular→universal, internal→external, …</code></pre> <p><small>Note: I manually labelled the clusters above.</small> I picked the top 4 clusters, averaged them, and saved the averages as the final 4 order vectors to choose from.</p> <p>If you want to see the actual values of the final order vectors, <a href="https://leanrada.com/notes/naturalest-sort/demo/order-vecs.json?ref=rss" target="_blank">check this out.</a></p> <p>To choose an order vector, we pick the one that spreads the input words the most along its axis. Large spread may not guarantee a meaningful ordering, but it’s good enough in practice, especially if the input words obviously have some scale.</p> <pre><code>// orderVecs contains the cluster averages of the top 4 clusters const ranked = orderVecs .map(orderVec =&gt; { // project the words onto this order vector const scores = wordVecs.map(v =&gt; dot(v, orderVec)); // calculate the spread const spread = Math.max(...scores) - Math.min(...scores) return { orderVec, spread }; }) // sort by spread .sort((a, b) =&gt; b.spread - a.spread); // get the order vector that most spreads the words orderVec = ranked[0].orderVec;</code></pre> <p>The algorithm can now choose to sort words based on the best fit order direction. For example, with these particular words the algorithm chooses the <code>magnitude</code> vector:</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 114"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'magnitude' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="56.54763572330071" y1="48" x2="56.54763572330071" y2="54" stroke-width="1"></line> , <line x1="70.83091689841137" y1="48" x2="70.83091689841137" y2="54" stroke-width="1"></line> , <line x1="85.11419807352203" y1="48" x2="85.11419807352203" y2="54" stroke-width="1"></line> , <line x1="99.3974792486327" y1="48" x2="99.3974792486327" y2="54" stroke-width="1"></line> , <line x1="113.68076042374337" y1="48" x2="113.68076042374337" y2="54" stroke-width="1"></line> , <line x1="127.96404159885402" y1="48" x2="127.96404159885402" y2="54" stroke-width="1"></line> , <line x1="142.2473227739647" y1="48" x2="142.2473227739647" y2="54" stroke-width="1"></line> , <line x1="156.53060394907536" y1="48" x2="156.53060394907536" y2="54" stroke-width="1"></line> , <line x1="170.813885124186" y1="48" x2="170.813885124186" y2="54" stroke-width="1"></line> , <line x1="185.09716629929665" y1="48" x2="185.09716629929665" y2="54" stroke-width="1"></line> , <line x1="199.3804474744073" y1="48" x2="199.3804474744073" y2="54" stroke-width="1"></line> , <line x1="213.66372864951794" y1="48" x2="213.66372864951794" y2="54" stroke-width="1"></line> , <line x1="227.94700982462857" y1="48" x2="227.94700982462857" y2="54" stroke-width="1"></line> , <line x1="242.23029099973925" y1="48" x2="242.23029099973925" y2="54" stroke-width="1"></line> , <line x1="256.5135721748499" y1="48" x2="256.5135721748499" y2="54" stroke-width="1"></line> , <line x1="270.79685334996054" y1="48" x2="270.79685334996054" y2="54" stroke-width="1"></line> , <line x1="285.08013452507123" y1="48" x2="285.08013452507123" y2="54" stroke-width="1"></line> , <line x1="299.36341570018186" y1="48" x2="299.36341570018186" y2="54" stroke-width="1"></line> , <line x1="313.6466968752925" y1="48" x2="313.6466968752925" y2="54" stroke-width="1"></line> , <line x1="327.9299780504032" y1="48" x2="327.9299780504032" y2="54" stroke-width="1"></line> , <line x1="342.21325922551387" y1="48" x2="342.21325922551387" y2="54" stroke-width="1"></line> , <line x1="356.4965404006245" y1="48" x2="356.4965404006245" y2="54" stroke-width="1"></line> , <line x1="370.7798215757351" y1="48" x2="370.7798215757351" y2="54" stroke-width="1"></line> <line x1="327.9299780504031" y1="48" x2="327.9299780504031" y2="54" stroke-width="1.5"></line> <text x="327.9299780504031" y="45" text-anchor="middle">0</text> <g> <circle cx="55.666666666666686" cy="51" r="7" fill="transparent"></circle> <circle cx="55.666666666666686" cy="51" r="3"></circle> <text x="55.666666666666686" y="44" text-anchor="middle">-0.19</text> <text x="52.666666666666686" y="84" text-anchor="end">low</text> <line x1="55.666666666666686" y1="54" x2="55.666666666666686" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="94.82277005274304" cy="51" r="7" fill="transparent"></circle> <circle cx="94.82277005274304" cy="51" r="3"></circle> <text x="94.82277005274304" y="44" text-anchor="middle">-0.16</text> <text x="91.82277005274304" y="96" text-anchor="end">medium</text> <line x1="94.82277005274304" y1="54" x2="94.82277005274304" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.03</text> <text x="369.3333333333333" y="108" text-anchor="end">high</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="108" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>low,<wbr>medium,<wbr>high</code> </td></tr></tbody></table> </figure> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 138"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'magnitude' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="67.3293602242297" y1="48" x2="67.3293602242297" y2="54" stroke-width="1"></line> , <line x1="95.78863966927852" y1="48" x2="95.78863966927852" y2="54" stroke-width="1"></line> , <line x1="124.24791911432735" y1="48" x2="124.24791911432735" y2="54" stroke-width="1"></line> , <line x1="152.70719855937617" y1="48" x2="152.70719855937617" y2="54" stroke-width="1"></line> , <line x1="181.16647800442502" y1="48" x2="181.16647800442502" y2="54" stroke-width="1"></line> , <line x1="209.62575744947387" y1="48" x2="209.62575744947387" y2="54" stroke-width="1"></line> , <line x1="238.08503689452266" y1="48" x2="238.08503689452266" y2="54" stroke-width="1"></line> , <line x1="266.54431633957154" y1="48" x2="266.54431633957154" y2="54" stroke-width="1"></line> , <line x1="295.00359578462036" y1="48" x2="295.00359578462036" y2="54" stroke-width="1"></line> , <line x1="323.4628752296692" y1="48" x2="323.4628752296692" y2="54" stroke-width="1"></line> , <line x1="351.922154674718" y1="48" x2="351.922154674718" y2="54" stroke-width="1"></line> , <line x1="380.3814341197668" y1="48" x2="380.3814341197668" y2="54" stroke-width="1"></line> <line x1="209.62575744947387" y1="48" x2="209.62575744947387" y2="54" stroke-width="1.5"></line> <text x="209.62575744947387" y="45" text-anchor="middle">0</text> <g> <circle cx="55.66666666666667" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666667" cy="51" r="3"></circle> <text x="55.66666666666667" y="44" text-anchor="middle">-0.27</text> <text x="52.66666666666667" y="84" text-anchor="end">tiny</text> <line x1="55.66666666666667" y1="54" x2="55.66666666666667" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="55.81266700857351" cy="51" r="7" fill="transparent"></circle> <circle cx="55.81266700857351" cy="51" r="3"></circle> <text x="55.81266700857351" y="44" text-anchor="middle">-0.27</text> <text x="52.81266700857351" y="96" text-anchor="end">small</text> <line x1="55.81266700857351" y1="54" x2="55.81266700857351" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="116.73305175071992" cy="51" r="7" fill="transparent"></circle> <circle cx="116.73305175071992" cy="51" r="3"></circle> <text x="116.73305175071992" y="44" text-anchor="middle">-0.16</text> <text x="113.73305175071992" y="108" text-anchor="end">medium</text> <line x1="116.73305175071992" y1="54" x2="116.73305175071992" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="248.38977505565046" cy="51" r="7" fill="transparent"></circle> <circle cx="248.38977505565046" cy="51" r="3"></circle> <text x="248.38977505565046" y="44" text-anchor="middle">0.07</text> <text x="245.38977505565046" y="120" text-anchor="end">large</text> <line x1="248.38977505565046" y1="54" x2="248.38977505565046" y2="120" stroke-width="1"></line> </g>,<g> <circle cx="372.33333333333337" cy="51" r="7" fill="transparent"></circle> <circle cx="372.33333333333337" cy="51" r="3"></circle> <text x="372.33333333333337" y="44" text-anchor="middle">0.29</text> <text x="369.33333333333337" y="132" text-anchor="end">huge</text> <line x1="372.33333333333337" y1="54" x2="372.33333333333337" y2="132" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>tiny,<wbr>small,<wbr>medium,<wbr>large,<wbr>huge</code> </td></tr></tbody></table> </figure> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 126"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'magnitude' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="48.52418612187938" y1="48" x2="48.52418612187938" y2="54" stroke-width="1"></line> , <line x1="61.75657548652295" y1="48" x2="61.75657548652295" y2="54" stroke-width="1"></line> , <line x1="74.98896485116651" y1="48" x2="74.98896485116651" y2="54" stroke-width="1"></line> , <line x1="88.22135421581008" y1="48" x2="88.22135421581008" y2="54" stroke-width="1"></line> , <line x1="101.45374358045365" y1="48" x2="101.45374358045365" y2="54" stroke-width="1"></line> , <line x1="114.6861329450972" y1="48" x2="114.6861329450972" y2="54" stroke-width="1"></line> , <line x1="127.91852230974077" y1="48" x2="127.91852230974077" y2="54" stroke-width="1"></line> , <line x1="141.15091167438433" y1="48" x2="141.15091167438433" y2="54" stroke-width="1"></line> , <line x1="154.38330103902788" y1="48" x2="154.38330103902788" y2="54" stroke-width="1"></line> , <line x1="167.61569040367146" y1="48" x2="167.61569040367146" y2="54" stroke-width="1"></line> , <line x1="180.84807976831502" y1="48" x2="180.84807976831502" y2="54" stroke-width="1"></line> , <line x1="194.08046913295857" y1="48" x2="194.08046913295857" y2="54" stroke-width="1"></line> , <line x1="207.31285849760212" y1="48" x2="207.31285849760212" y2="54" stroke-width="1"></line> , <line x1="220.54524786224567" y1="48" x2="220.54524786224567" y2="54" stroke-width="1"></line> , <line x1="233.7776372268892" y1="48" x2="233.7776372268892" y2="54" stroke-width="1"></line> , <line x1="247.01002659153275" y1="48" x2="247.01002659153275" y2="54" stroke-width="1"></line> , <line x1="260.2424159561763" y1="48" x2="260.2424159561763" y2="54" stroke-width="1"></line> , <line x1="273.4748053208199" y1="48" x2="273.4748053208199" y2="54" stroke-width="1"></line> , <line x1="286.70719468546343" y1="48" x2="286.70719468546343" y2="54" stroke-width="1"></line> , <line x1="299.93958405010693" y1="48" x2="299.93958405010693" y2="54" stroke-width="1"></line> , <line x1="313.1719734147505" y1="48" x2="313.1719734147505" y2="54" stroke-width="1"></line> , <line x1="326.40436277939403" y1="48" x2="326.40436277939403" y2="54" stroke-width="1"></line> , <line x1="339.63675214403764" y1="48" x2="339.63675214403764" y2="54" stroke-width="1"></line> , <line x1="352.86914150868114" y1="48" x2="352.86914150868114" y2="54" stroke-width="1"></line> , <line x1="366.10153087332475" y1="48" x2="366.10153087332475" y2="54" stroke-width="1"></line> , <line x1="379.33392023796824" y1="48" x2="379.33392023796824" y2="54" stroke-width="1"></line> <line x1="366.1015308733246" y1="48" x2="366.1015308733246" y2="54" stroke-width="1.5"></line> <text x="366.1015308733246" y="45" text-anchor="middle">0</text> <g> <circle cx="55.66666666666666" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666666" cy="51" r="3"></circle> <text x="55.66666666666666" y="44" text-anchor="middle">-0.23</text> <text x="52.66666666666666" y="84" text-anchor="end">whisper</text> <line x1="55.66666666666666" y1="54" x2="55.66666666666666" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="281.34499939918385" cy="51" r="7" fill="transparent"></circle> <circle cx="281.34499939918385" cy="51" r="3"></circle> <text x="281.34499939918385" y="44" text-anchor="middle">-0.06</text> <text x="278.34499939918385" y="96" text-anchor="end">talk</text> <line x1="281.34499939918385" y1="54" x2="281.34499939918385" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="313.87739820406335" cy="51" r="7" fill="transparent"></circle> <circle cx="313.87739820406335" cy="51" r="3"></circle> <text x="313.87739820406335" y="44" text-anchor="middle">-0.04</text> <text x="310.87739820406335" y="108" text-anchor="end">shout</text> <line x1="313.87739820406335" y1="54" x2="313.87739820406335" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.00</text> <text x="369.3333333333333" y="120" text-anchor="end">scream</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="120" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>whisper,<wbr>talk,<wbr>shout,<wbr>scream</code> </td></tr></tbody></table> </figure> <p>The following words fall into the <code>value</code> vector, the good/bad axis:</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 126"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'value' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="56.37154739248518" y1="48" x2="56.37154739248518" y2="54" stroke-width="1"></line> , <line x1="76.28065288930938" y1="48" x2="76.28065288930938" y2="54" stroke-width="1"></line> , <line x1="96.18975838613358" y1="48" x2="96.18975838613358" y2="54" stroke-width="1"></line> , <line x1="116.0988638829578" y1="48" x2="116.0988638829578" y2="54" stroke-width="1"></line> , <line x1="136.007969379782" y1="48" x2="136.007969379782" y2="54" stroke-width="1"></line> , <line x1="155.9170748766062" y1="48" x2="155.9170748766062" y2="54" stroke-width="1"></line> , <line x1="175.82618037343042" y1="48" x2="175.82618037343042" y2="54" stroke-width="1"></line> , <line x1="195.73528587025464" y1="48" x2="195.73528587025464" y2="54" stroke-width="1"></line> , <line x1="215.64439136707887" y1="48" x2="215.64439136707887" y2="54" stroke-width="1"></line> , <line x1="235.55349686390306" y1="48" x2="235.55349686390306" y2="54" stroke-width="1"></line> , <line x1="255.46260236072726" y1="48" x2="255.46260236072726" y2="54" stroke-width="1"></line> , <line x1="275.3717078575514" y1="48" x2="275.3717078575514" y2="54" stroke-width="1"></line> , <line x1="295.28081335437565" y1="48" x2="295.28081335437565" y2="54" stroke-width="1"></line> , <line x1="315.18991885119993" y1="48" x2="315.18991885119993" y2="54" stroke-width="1"></line> , <line x1="335.0990243480241" y1="48" x2="335.0990243480241" y2="54" stroke-width="1"></line> , <line x1="355.0081298448484" y1="48" x2="355.0081298448484" y2="54" stroke-width="1"></line> , <line x1="374.91723534167255" y1="48" x2="374.91723534167255" y2="54" stroke-width="1"></line> <line x1="235.55349686390306" y1="48" x2="235.55349686390306" y2="54" stroke-width="1.5"></line> <text x="235.55349686390306" y="45" text-anchor="middle">0</text> <g> <circle cx="55.666666666666664" cy="51" r="7" fill="transparent"></circle> <circle cx="55.666666666666664" cy="51" r="3"></circle> <text x="55.666666666666664" y="44" text-anchor="middle">-0.18</text> <text x="52.666666666666664" y="84" text-anchor="end">hate</text> <line x1="55.666666666666664" y1="54" x2="55.666666666666664" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="133.6580190025121" cy="51" r="7" fill="transparent"></circle> <circle cx="133.6580190025121" cy="51" r="3"></circle> <text x="133.6580190025121" y="44" text-anchor="middle">-0.10</text> <text x="130.6580190025121" y="96" text-anchor="end">dislike</text> <line x1="133.6580190025121" y1="54" x2="133.6580190025121" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="301.18768540445166" cy="51" r="7" fill="transparent"></circle> <circle cx="301.18768540445166" cy="51" r="3"></circle> <text x="301.18768540445166" y="44" text-anchor="middle">0.07</text> <text x="298.18768540445166" y="108" text-anchor="end">like</text> <line x1="301.18768540445166" y1="54" x2="301.18768540445166" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.14</text> <text x="369.3333333333333" y="120" text-anchor="end">love</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="120" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>hate,<wbr>dislike,<wbr>like,<wbr>love</code> </td></tr></tbody></table> </figure> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 126"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'value' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="54.26264255658401" y1="48" x2="54.26264255658401" y2="54" stroke-width="1"></line> , <line x1="81.70689073797061" y1="48" x2="81.70689073797061" y2="54" stroke-width="1"></line> , <line x1="109.15113891935724" y1="48" x2="109.15113891935724" y2="54" stroke-width="1"></line> , <line x1="136.59538710074384" y1="48" x2="136.59538710074384" y2="54" stroke-width="1"></line> , <line x1="164.03963528213043" y1="48" x2="164.03963528213043" y2="54" stroke-width="1"></line> , <line x1="191.48388346351706" y1="48" x2="191.48388346351706" y2="54" stroke-width="1"></line> , <line x1="218.92813164490371" y1="48" x2="218.92813164490371" y2="54" stroke-width="1"></line> , <line x1="246.37237982629028" y1="48" x2="246.37237982629028" y2="54" stroke-width="1"></line> , <line x1="273.81662800767697" y1="48" x2="273.81662800767697" y2="54" stroke-width="1"></line> , <line x1="301.26087618906354" y1="48" x2="301.26087618906354" y2="54" stroke-width="1"></line> , <line x1="328.7051243704502" y1="48" x2="328.7051243704502" y2="54" stroke-width="1"></line> , <line x1="356.1493725518368" y1="48" x2="356.1493725518368" y2="54" stroke-width="1"></line> , <line x1="383.59362073322336" y1="48" x2="383.59362073322336" y2="54" stroke-width="1"></line> <line x1="218.92813164490371" y1="48" x2="218.92813164490371" y2="54" stroke-width="1.5"></line> <text x="218.92813164490371" y="45" text-anchor="middle">0</text> <g> <circle cx="55.666666666666664" cy="51" r="7" fill="transparent"></circle> <circle cx="55.666666666666664" cy="51" r="3"></circle> <text x="55.666666666666664" y="44" text-anchor="middle">-0.30</text> <text x="52.666666666666664" y="84" text-anchor="end">ugly</text> <line x1="55.666666666666664" y1="54" x2="55.666666666666664" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="122.0702285449164" cy="51" r="7" fill="transparent"></circle> <circle cx="122.0702285449164" cy="51" r="3"></circle> <text x="122.0702285449164" y="44" text-anchor="middle">-0.18</text> <text x="119.0702285449164" y="96" text-anchor="end">plain</text> <line x1="122.0702285449164" y1="54" x2="122.0702285449164" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="264.4972089435215" cy="51" r="7" fill="transparent"></circle> <circle cx="264.4972089435215" cy="51" r="3"></circle> <text x="264.4972089435215" y="44" text-anchor="middle">0.08</text> <text x="261.4972089435215" y="108" text-anchor="end">pretty</text> <line x1="264.4972089435215" y1="54" x2="264.4972089435215" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.28</text> <text x="369.3333333333333" y="120" text-anchor="end">beautiful</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="120" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>ugly,<wbr>plain,<wbr>pretty,<wbr>beautiful</code> </td></tr></tbody></table> </figure> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 126"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'value' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="64.34956446790088" y1="48" x2="64.34956446790088" y2="54" stroke-width="1"></line> , <line x1="88.3225109689476" y1="48" x2="88.3225109689476" y2="54" stroke-width="1"></line> , <line x1="112.29545746999433" y1="48" x2="112.29545746999433" y2="54" stroke-width="1"></line> , <line x1="136.26840397104107" y1="48" x2="136.26840397104107" y2="54" stroke-width="1"></line> , <line x1="160.24135047208782" y1="48" x2="160.24135047208782" y2="54" stroke-width="1"></line> , <line x1="184.21429697313454" y1="48" x2="184.21429697313454" y2="54" stroke-width="1"></line> , <line x1="208.1872434741813" y1="48" x2="208.1872434741813" y2="54" stroke-width="1"></line> , <line x1="232.160189975228" y1="48" x2="232.160189975228" y2="54" stroke-width="1"></line> , <line x1="256.1331364762748" y1="48" x2="256.1331364762748" y2="54" stroke-width="1"></line> , <line x1="280.10608297732153" y1="48" x2="280.10608297732153" y2="54" stroke-width="1"></line> , <line x1="304.07902947836817" y1="48" x2="304.07902947836817" y2="54" stroke-width="1"></line> , <line x1="328.0519759794149" y1="48" x2="328.0519759794149" y2="54" stroke-width="1"></line> , <line x1="352.0249224804617" y1="48" x2="352.0249224804617" y2="54" stroke-width="1"></line> , <line x1="375.9978689815084" y1="48" x2="375.9978689815084" y2="54" stroke-width="1"></line> <line x1="184.21429697313454" y1="48" x2="184.21429697313454" y2="54" stroke-width="1.5"></line> <text x="184.21429697313454" y="45" text-anchor="middle">0</text> <g> <circle cx="55.66666666666666" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666666" cy="51" r="3"></circle> <text x="55.66666666666666" y="44" text-anchor="middle">-0.27</text> <text x="52.66666666666666" y="84" text-anchor="end">poor</text> <line x1="55.66666666666666" y1="54" x2="55.66666666666666" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="234.5993795028003" cy="51" r="7" fill="transparent"></circle> <circle cx="234.5993795028003" cy="51" r="3"></circle> <text x="234.5993795028003" y="44" text-anchor="middle">0.11</text> <text x="231.5993795028003" y="96" text-anchor="end">fair</text> <line x1="234.5993795028003" y1="54" x2="234.5993795028003" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="288.9270795784592" cy="51" r="7" fill="transparent"></circle> <circle cx="288.9270795784592" cy="51" r="3"></circle> <text x="288.9270795784592" y="44" text-anchor="middle">0.22</text> <text x="285.9270795784592" y="108" text-anchor="end">good</text> <line x1="288.9270795784592" y1="54" x2="288.9270795784592" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.39</text> <text x="369.3333333333333" y="120" text-anchor="end">excellent</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="120" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>poor,<wbr>fair,<wbr>good,<wbr>excellent</code> </td></tr></tbody></table> </figure> <p>Here are some words sorted by the <code>sequence</code> vector:</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 114"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'sequence' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="57.05199524328027" y1="48" x2="57.05199524328027" y2="54" stroke-width="1"></line> , <line x1="76.26655547213707" y1="48" x2="76.26655547213707" y2="54" stroke-width="1"></line> , <line x1="95.4811157009939" y1="48" x2="95.4811157009939" y2="54" stroke-width="1"></line> , <line x1="114.69567592985071" y1="48" x2="114.69567592985071" y2="54" stroke-width="1"></line> , <line x1="133.9102361587075" y1="48" x2="133.9102361587075" y2="54" stroke-width="1"></line> , <line x1="153.12479638756434" y1="48" x2="153.12479638756434" y2="54" stroke-width="1"></line> , <line x1="172.33935661642116" y1="48" x2="172.33935661642116" y2="54" stroke-width="1"></line> , <line x1="191.55391684527797" y1="48" x2="191.55391684527797" y2="54" stroke-width="1"></line> , <line x1="210.76847707413478" y1="48" x2="210.76847707413478" y2="54" stroke-width="1"></line> , <line x1="229.9830373029916" y1="48" x2="229.9830373029916" y2="54" stroke-width="1"></line> , <line x1="249.19759753184834" y1="48" x2="249.19759753184834" y2="54" stroke-width="1"></line> , <line x1="268.4121577607051" y1="48" x2="268.4121577607051" y2="54" stroke-width="1"></line> , <line x1="287.62671798956194" y1="48" x2="287.62671798956194" y2="54" stroke-width="1"></line> , <line x1="306.8412782184187" y1="48" x2="306.8412782184187" y2="54" stroke-width="1"></line> , <line x1="326.0558384472755" y1="48" x2="326.0558384472755" y2="54" stroke-width="1"></line> , <line x1="345.2703986761323" y1="48" x2="345.2703986761323" y2="54" stroke-width="1"></line> , <line x1="364.48495890498907" y1="48" x2="364.48495890498907" y2="54" stroke-width="1"></line> , <line x1="383.6995191338459" y1="48" x2="383.6995191338459" y2="54" stroke-width="1"></line> <g> <circle cx="55.66666666666666" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666666" cy="51" r="3"></circle> <text x="55.66666666666666" y="44" text-anchor="middle">-0.11</text> <text x="52.66666666666666" y="84" text-anchor="end">dawn</text> <line x1="55.66666666666666" y1="54" x2="55.66666666666666" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="273.5737035555543" cy="51" r="7" fill="transparent"></circle> <circle cx="273.5737035555543" cy="51" r="3"></circle> <text x="273.5737035555543" y="44" text-anchor="middle">-0.05</text> <text x="270.5737035555543" y="96" text-anchor="end">noon</text> <line x1="273.5737035555543" y1="54" x2="273.5737035555543" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">-0.02</text> <text x="369.3333333333333" y="108" text-anchor="end">dusk</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="108" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>dawn,<wbr>noon,<wbr>dusk</code> </td></tr></tbody></table> </figure> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 114"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'sequence' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="54.77117862850875" y1="48" x2="54.77117862850875" y2="54" stroke-width="1"></line> , <line x1="69.6214642839255" y1="48" x2="69.6214642839255" y2="54" stroke-width="1"></line> , <line x1="84.47174993934227" y1="48" x2="84.47174993934227" y2="54" stroke-width="1"></line> , <line x1="99.32203559475903" y1="48" x2="99.32203559475903" y2="54" stroke-width="1"></line> , <line x1="114.17232125017578" y1="48" x2="114.17232125017578" y2="54" stroke-width="1"></line> , <line x1="129.0226069055925" y1="48" x2="129.0226069055925" y2="54" stroke-width="1"></line> , <line x1="143.87289256100928" y1="48" x2="143.87289256100928" y2="54" stroke-width="1"></line> , <line x1="158.723178216426" y1="48" x2="158.723178216426" y2="54" stroke-width="1"></line> , <line x1="173.57346387184276" y1="48" x2="173.57346387184276" y2="54" stroke-width="1"></line> , <line x1="188.42374952725947" y1="48" x2="188.42374952725947" y2="54" stroke-width="1"></line> , <line x1="203.27403518267622" y1="48" x2="203.27403518267622" y2="54" stroke-width="1"></line> , <line x1="218.12432083809298" y1="48" x2="218.12432083809298" y2="54" stroke-width="1"></line> , <line x1="232.9746064935097" y1="48" x2="232.9746064935097" y2="54" stroke-width="1"></line> , <line x1="247.82489214892644" y1="48" x2="247.82489214892644" y2="54" stroke-width="1"></line> , <line x1="262.6751778043432" y1="48" x2="262.6751778043432" y2="54" stroke-width="1"></line> , <line x1="277.52546345976" y1="48" x2="277.52546345976" y2="54" stroke-width="1"></line> , <line x1="292.3757491151767" y1="48" x2="292.3757491151767" y2="54" stroke-width="1"></line> , <line x1="307.22603477059346" y1="48" x2="307.22603477059346" y2="54" stroke-width="1"></line> , <line x1="322.0763204260102" y1="48" x2="322.0763204260102" y2="54" stroke-width="1"></line> , <line x1="336.92660608142694" y1="48" x2="336.92660608142694" y2="54" stroke-width="1"></line> , <line x1="351.7768917368437" y1="48" x2="351.7768917368437" y2="54" stroke-width="1"></line> , <line x1="366.6271773922605" y1="48" x2="366.6271773922605" y2="54" stroke-width="1"></line> , <line x1="381.47746304767725" y1="48" x2="381.47746304767725" y2="54" stroke-width="1"></line> <line x1="307.22603477059346" y1="48" x2="307.22603477059346" y2="54" stroke-width="1.5"></line> <text x="307.22603477059346" y="45" text-anchor="middle">0</text> <g> <circle cx="55.66666666666666" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666666" cy="51" r="3"></circle> <text x="55.66666666666666" y="44" text-anchor="middle">-0.17</text> <text x="52.66666666666666" y="84" text-anchor="end">birth</text> <line x1="55.66666666666666" y1="54" x2="55.66666666666666" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="77.38526979126134" cy="51" r="7" fill="transparent"></circle> <circle cx="77.38526979126134" cy="51" r="3"></circle> <text x="77.38526979126134" y="44" text-anchor="middle">-0.15</text> <text x="74.38526979126134" y="96" text-anchor="end">life</text> <line x1="77.38526979126134" y1="54" x2="77.38526979126134" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.04</text> <text x="369.3333333333333" y="108" text-anchor="end">death</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="108" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>birth,<wbr>life,<wbr>death</code> </td></tr></tbody></table> </figure> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 114"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'sequence' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="54.20668680261805" y1="48" x2="54.20668680261805" y2="54" stroke-width="1"></line> , <line x1="71.60943914310424" y1="48" x2="71.60943914310424" y2="54" stroke-width="1"></line> , <line x1="89.01219148359041" y1="48" x2="89.01219148359041" y2="54" stroke-width="1"></line> , <line x1="106.41494382407659" y1="48" x2="106.41494382407659" y2="54" stroke-width="1"></line> , <line x1="123.81769616456276" y1="48" x2="123.81769616456276" y2="54" stroke-width="1"></line> , <line x1="141.22044850504895" y1="48" x2="141.22044850504895" y2="54" stroke-width="1"></line> , <line x1="158.62320084553514" y1="48" x2="158.62320084553514" y2="54" stroke-width="1"></line> , <line x1="176.02595318602133" y1="48" x2="176.02595318602133" y2="54" stroke-width="1"></line> , <line x1="193.42870552650749" y1="48" x2="193.42870552650749" y2="54" stroke-width="1"></line> , <line x1="210.83145786699367" y1="48" x2="210.83145786699367" y2="54" stroke-width="1"></line> , <line x1="228.2342102074798" y1="48" x2="228.2342102074798" y2="54" stroke-width="1"></line> , <line x1="245.63696254796602" y1="48" x2="245.63696254796602" y2="54" stroke-width="1"></line> , <line x1="263.0397148884522" y1="48" x2="263.0397148884522" y2="54" stroke-width="1"></line> , <line x1="280.44246722893837" y1="48" x2="280.44246722893837" y2="54" stroke-width="1"></line> , <line x1="297.84521956942456" y1="48" x2="297.84521956942456" y2="54" stroke-width="1"></line> , <line x1="315.24797190991075" y1="48" x2="315.24797190991075" y2="54" stroke-width="1"></line> , <line x1="332.65072425039693" y1="48" x2="332.65072425039693" y2="54" stroke-width="1"></line> , <line x1="350.0534765908832" y1="48" x2="350.0534765908832" y2="54" stroke-width="1"></line> , <line x1="367.45622893136937" y1="48" x2="367.45622893136937" y2="54" stroke-width="1"></line> , <line x1="384.8589812718555" y1="48" x2="384.8589812718555" y2="54" stroke-width="1"></line> <line x1="54.20668680261805" y1="48" x2="54.20668680261805" y2="54" stroke-width="1.5"></line> <text x="54.20668680261805" y="45" text-anchor="middle">0</text> <g> <circle cx="55.666666666666664" cy="51" r="7" fill="transparent"></circle> <circle cx="55.666666666666664" cy="51" r="3"></circle> <text x="55.666666666666664" y="44" text-anchor="middle">0.00</text> <text x="52.666666666666664" y="84" text-anchor="end">once</text> <line x1="55.666666666666664" y1="54" x2="55.666666666666664" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="282.4706992225206" cy="51" r="7" fill="transparent"></circle> <circle cx="282.4706992225206" cy="51" r="3"></circle> <text x="282.4706992225206" y="44" text-anchor="middle">0.13</text> <text x="279.4706992225206" y="96" text-anchor="end">twice</text> <line x1="282.4706992225206" y1="54" x2="282.4706992225206" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.18</text> <text x="369.3333333333333" y="108" text-anchor="end">thrice</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="108" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>once,<wbr>twice,<wbr>thrice</code> </td></tr></tbody></table> </figure> <p>Interestingly, the particular metals bronze, silver, and gold are sorted as <em>medals</em> in first, second, and third places instead of increasing value. The choice of order vector being the <code>sequence</code> vector explains this.</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 114"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'sequence' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="45.74567147516805" y1="48" x2="45.74567147516805" y2="54" stroke-width="1"></line> , <line x1="67.4913429503361" y1="48" x2="67.4913429503361" y2="54" stroke-width="1"></line> , <line x1="89.23701442550416" y1="48" x2="89.23701442550416" y2="54" stroke-width="1"></line> , <line x1="110.98268590067221" y1="48" x2="110.98268590067221" y2="54" stroke-width="1"></line> , <line x1="132.7283573758403" y1="48" x2="132.7283573758403" y2="54" stroke-width="1"></line> , <line x1="154.47402885100834" y1="48" x2="154.47402885100834" y2="54" stroke-width="1"></line> , <line x1="176.2197003261764" y1="48" x2="176.2197003261764" y2="54" stroke-width="1"></line> , <line x1="197.96537180134442" y1="48" x2="197.96537180134442" y2="54" stroke-width="1"></line> , <line x1="219.71104327651247" y1="48" x2="219.71104327651247" y2="54" stroke-width="1"></line> , <line x1="241.4567147516805" y1="48" x2="241.4567147516805" y2="54" stroke-width="1"></line> , <line x1="263.20238622684855" y1="48" x2="263.20238622684855" y2="54" stroke-width="1"></line> , <line x1="284.94805770201657" y1="48" x2="284.94805770201657" y2="54" stroke-width="1"></line> , <line x1="306.69372917718465" y1="48" x2="306.69372917718465" y2="54" stroke-width="1"></line> , <line x1="328.4394006523527" y1="48" x2="328.4394006523527" y2="54" stroke-width="1"></line> , <line x1="350.18507212752075" y1="48" x2="350.18507212752075" y2="54" stroke-width="1"></line> , <line x1="371.93074360268884" y1="48" x2="371.93074360268884" y2="54" stroke-width="1"></line> <g> <circle cx="146.05570715965317" cy="51" r="7" fill="transparent"></circle> <circle cx="146.05570715965317" cy="51" r="3"></circle> <text x="146.05570715965317" y="44" text-anchor="middle">0.06</text> <text x="143.05570715965317" y="84" text-anchor="end">gold</text> <line x1="146.05570715965317" y1="54" x2="146.05570715965317" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="212.74416274356537" cy="51" r="7" fill="transparent"></circle> <circle cx="212.74416274356537" cy="51" r="3"></circle> <text x="212.74416274356537" y="44" text-anchor="middle">0.09</text> <text x="209.74416274356537" y="96" text-anchor="end">silver</text> <line x1="212.74416274356537" y1="54" x2="212.74416274356537" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="380.5505188326958" cy="51" r="7" fill="transparent"></circle> <circle cx="380.5505188326958" cy="51" r="3"></circle> <text x="380.5505188326958" y="44" text-anchor="middle">0.16</text> <text x="377.5505188326958" y="108" text-anchor="end">bronze</text> <line x1="380.5505188326958" y1="54" x2="380.5505188326958" y2="108" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>gold,<wbr>silver,<wbr>bronze</code> </td></tr></tbody></table> </figure> <p>Ironically though, the ordinals aren’t quite correct:</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 138"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'sequence' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="50.07048810935012" y1="48" x2="50.07048810935012" y2="54" stroke-width="1"></line> , <line x1="63.873898883177695" y1="48" x2="63.873898883177695" y2="54" stroke-width="1"></line> , <line x1="77.67730965700528" y1="48" x2="77.67730965700528" y2="54" stroke-width="1"></line> , <line x1="91.48072043083286" y1="48" x2="91.48072043083286" y2="54" stroke-width="1"></line> , <line x1="105.28413120466044" y1="48" x2="105.28413120466044" y2="54" stroke-width="1"></line> , <line x1="119.08754197848802" y1="48" x2="119.08754197848802" y2="54" stroke-width="1"></line> , <line x1="132.89095275231557" y1="48" x2="132.89095275231557" y2="54" stroke-width="1"></line> , <line x1="146.69436352614312" y1="48" x2="146.69436352614312" y2="54" stroke-width="1"></line> , <line x1="160.4977742999707" y1="48" x2="160.4977742999707" y2="54" stroke-width="1"></line> , <line x1="174.30118507379825" y1="48" x2="174.30118507379825" y2="54" stroke-width="1"></line> , <line x1="188.1045958476258" y1="48" x2="188.1045958476258" y2="54" stroke-width="1"></line> , <line x1="201.90800662145338" y1="48" x2="201.90800662145338" y2="54" stroke-width="1"></line> , <line x1="215.71141739528093" y1="48" x2="215.71141739528093" y2="54" stroke-width="1"></line> , <line x1="229.51482816910845" y1="48" x2="229.51482816910845" y2="54" stroke-width="1"></line> , <line x1="243.31823894293606" y1="48" x2="243.31823894293606" y2="54" stroke-width="1"></line> , <line x1="257.1216497167636" y1="48" x2="257.1216497167636" y2="54" stroke-width="1"></line> , <line x1="270.9250604905912" y1="48" x2="270.9250604905912" y2="54" stroke-width="1"></line> , <line x1="284.7284712644188" y1="48" x2="284.7284712644188" y2="54" stroke-width="1"></line> , <line x1="298.53188203824635" y1="48" x2="298.53188203824635" y2="54" stroke-width="1"></line> , <line x1="312.33529281207393" y1="48" x2="312.33529281207393" y2="54" stroke-width="1"></line> , <line x1="326.13870358590145" y1="48" x2="326.13870358590145" y2="54" stroke-width="1"></line> , <line x1="339.94211435972903" y1="48" x2="339.94211435972903" y2="54" stroke-width="1"></line> , <line x1="353.7455251335566" y1="48" x2="353.7455251335566" y2="54" stroke-width="1"></line> , <line x1="367.5489359073842" y1="48" x2="367.5489359073842" y2="54" stroke-width="1"></line> , <line x1="381.3523466812117" y1="48" x2="381.3523466812117" y2="54" stroke-width="1"></line> <line x1="298.5318820382463" y1="48" x2="298.5318820382463" y2="54" stroke-width="1.5"></line> <text x="298.5318820382463" y="45" text-anchor="middle">0</text> <g> <circle cx="55.666666666666664" cy="51" r="7" fill="transparent"></circle> <circle cx="55.666666666666664" cy="51" r="3"></circle> <text x="55.666666666666664" y="44" text-anchor="middle">-0.18</text> <text x="52.666666666666664" y="84" text-anchor="end">first</text> <line x1="55.666666666666664" y1="54" x2="55.666666666666664" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="272.6145594245852" cy="51" r="7" fill="transparent"></circle> <circle cx="272.6145594245852" cy="51" r="3"></circle> <text x="272.6145594245852" y="44" text-anchor="middle">-0.02</text> <text x="269.6145594245852" y="96" text-anchor="end">second</text> <line x1="272.6145594245852" y1="54" x2="272.6145594245852" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="322.0081804864052" cy="51" r="7" fill="transparent"></circle> <circle cx="322.0081804864052" cy="51" r="3"></circle> <text x="322.0081804864052" y="44" text-anchor="middle">0.02</text> <text x="319.0081804864052" y="108" text-anchor="end">fifth</text> <line x1="322.0081804864052" y1="54" x2="322.0081804864052" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="360.1451796191923" cy="51" r="7" fill="transparent"></circle> <circle cx="360.1451796191923" cy="51" r="3"></circle> <text x="360.1451796191923" y="44" text-anchor="middle">0.04</text> <text x="357.1451796191923" y="120" text-anchor="end">third</text> <line x1="360.1451796191923" y1="54" x2="360.1451796191923" y2="120" stroke-width="1"></line> </g>,<g> <circle cx="372.33333333333326" cy="51" r="7" fill="transparent"></circle> <circle cx="372.33333333333326" cy="51" r="3"></circle> <text x="372.33333333333326" y="44" text-anchor="middle">0.05</text> <text x="369.33333333333326" y="132" text-anchor="end">fourth</text> <line x1="372.33333333333326" y1="54" x2="372.33333333333326" y2="132" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>first,<wbr>second,<wbr>fifth,<wbr>third,<wbr>fourth</code> </td></tr></tbody></table> </figure> <p>The choice of order vector for ordinals seems correct, but it went wrong with <code>"fifth"</code>.</p> <p>The 2D plot via principal component analysis (PCA) reveals an interesting pattern. Even though the ordinals appear to maintain a clear sequence and shape in conceptual space, halfway through they turn hard left.</p> <figure> <svg viewBox="0 0 400 400"><defs> <marker id="scatter-arrow-end" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> <marker id="scatter-arrow-start" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> </defs> <line x1="33.6" y1="200" x2="366.4" y2="200" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="200" y1="338.5" x2="200" y2="61.5" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="93.8" y1="197" x2="93.8" y2="203" stroke-width="1"></line><line x1="129.1" y1="197" x2="129.1" y2="203" stroke-width="1"></line><line x1="164.5" y1="197" x2="164.5" y2="203" stroke-width="1"></line><line x1="199.8" y1="197" x2="199.8" y2="203" stroke-width="1"></line><line x1="235.1" y1="197" x2="235.1" y2="203" stroke-width="1"></line><line x1="270.5" y1="197" x2="270.5" y2="203" stroke-width="1"></line><line x1="305.8" y1="197" x2="305.8" y2="203" stroke-width="1"></line><line x1="197" y1="293.2" x2="203" y2="293.2" stroke-width="1"></line><line x1="197" y1="257.9" x2="203" y2="257.9" stroke-width="1"></line><line x1="197" y1="222.5" x2="203" y2="222.5" stroke-width="1"></line><line x1="197" y1="187.2" x2="203" y2="187.2" stroke-width="1"></line><line x1="197" y1="151.8" x2="203" y2="151.8" stroke-width="1"></line><line x1="197" y1="116.5" x2="203" y2="116.5" stroke-width="1"></line><g> <circle cx="48.0" cy="75.9" r="7" fill="transparent"></circle> <circle cx="48.0" cy="75.9" r="3"></circle> <text x="55.0" y="79.9" text-anchor="start">first</text> <text x="55.0" y="89.9" text-anchor="start">(-0.53, 0.41)</text> </g>,<g> <circle cx="121.8" cy="250.1" r="7" fill="transparent"></circle> <circle cx="121.8" cy="250.1" r="3"></circle> <text x="128.8" y="254.1" text-anchor="start">second</text> <text x="128.8" y="264.1" text-anchor="start">(-0.32, -0.08)</text> </g>,<g> <circle cx="143.7" cy="324.1" r="7" fill="transparent"></circle> <circle cx="143.7" cy="324.1" r="3"></circle> <text x="150.7" y="328.1" text-anchor="start">third</text> <text x="150.7" y="338.1" text-anchor="start">(-0.26, -0.29)</text> </g>,<g> <circle cx="173.4" cy="319.3" r="7" fill="transparent"></circle> <circle cx="173.4" cy="319.3" r="3"></circle> <text x="180.4" y="323.3" text-anchor="start">fourth</text> <text x="180.4" y="333.3" text-anchor="start">(-0.17, -0.27)</text> </g>,<g> <circle cx="279.9" cy="231.4" r="7" fill="transparent"></circle> <circle cx="279.9" cy="231.4" r="3"></circle> <text x="272.9" y="235.4" text-anchor="end">fifth</text> <text x="272.9" y="245.4" text-anchor="end">(0.13, -0.02)</text> </g>,<g> <circle cx="314.5" cy="209.8" r="7" fill="transparent"></circle> <circle cx="314.5" cy="209.8" r="3"></circle> <text x="307.5" y="213.8" text-anchor="end">sixth</text> <text x="307.5" y="223.8" text-anchor="end">(0.22, 0.04)</text> </g>,<g> <circle cx="335.3" cy="204.8" r="7" fill="transparent"></circle> <circle cx="335.3" cy="204.8" r="3"></circle> <text x="328.3" y="208.8" text-anchor="end">seventh</text> <text x="328.3" y="218.8" text-anchor="end">(0.28, 0.05)</text> </g>,<g> <circle cx="347.5" cy="200.9" r="7" fill="transparent"></circle> <circle cx="347.5" cy="200.9" r="3"></circle> <text x="340.5" y="204.9" text-anchor="end">eighth</text> <text x="340.5" y="214.9" text-anchor="end">(0.32, 0.06)</text> </g>,<g> <circle cx="352.0" cy="186.4" r="7" fill="transparent"></circle> <circle cx="352.0" cy="186.4" r="3"></circle> <text x="345.0" y="190.4" text-anchor="end">ninth</text> <text x="345.0" y="200.4" text-anchor="end">(0.33, 0.10)</text> </g></svg> </figure> <p>The <code>sequence</code> vector may have only captured the initial direction of the ordinals. Indeed the anchors <code>"first","last"</code> are included in the <code>sequence</code> cluster, as well as synonyms/neighbors like <code>"initial"</code> and <code>"early"</code>.</p> <p>The frequency vector is the weakest of the top 4 clusters, but I included it to squeeze some more coverage:</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 138"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'frequency' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="48.68917529112011" y1="48" x2="48.68917529112011" y2="54" stroke-width="1"></line> , <line x1="79.24968092355041" y1="48" x2="79.24968092355041" y2="54" stroke-width="1"></line> , <line x1="109.8101865559807" y1="48" x2="109.8101865559807" y2="54" stroke-width="1"></line> , <line x1="140.370692188411" y1="48" x2="140.370692188411" y2="54" stroke-width="1"></line> , <line x1="170.93119782084133" y1="48" x2="170.93119782084133" y2="54" stroke-width="1"></line> , <line x1="201.49170345327164" y1="48" x2="201.49170345327164" y2="54" stroke-width="1"></line> , <line x1="232.05220908570195" y1="48" x2="232.05220908570195" y2="54" stroke-width="1"></line> , <line x1="262.6127147181322" y1="48" x2="262.6127147181322" y2="54" stroke-width="1"></line> , <line x1="293.17322035056253" y1="48" x2="293.17322035056253" y2="54" stroke-width="1"></line> , <line x1="323.7337259829929" y1="48" x2="323.7337259829929" y2="54" stroke-width="1"></line> , <line x1="354.29423161542314" y1="48" x2="354.29423161542314" y2="54" stroke-width="1"></line> , <line x1="384.8547372478535" y1="48" x2="384.8547372478535" y2="54" stroke-width="1"></line> <line x1="201.49170345327164" y1="48" x2="201.49170345327164" y2="54" stroke-width="1.5"></line> <text x="201.49170345327164" y="45" text-anchor="middle">0</text> <g> <circle cx="55.66666666666666" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666666" cy="51" r="3"></circle> <text x="55.66666666666666" y="44" text-anchor="middle">-0.24</text> <text x="52.66666666666666" y="84" text-anchor="end">none</text> <line x1="55.66666666666666" y1="54" x2="55.66666666666666" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="195.7151392507283" cy="51" r="7" fill="transparent"></circle> <circle cx="195.7151392507283" cy="51" r="3"></circle> <text x="195.7151392507283" y="44" text-anchor="middle">-0.01</text> <text x="192.7151392507283" y="96" text-anchor="end">few</text> <line x1="195.7151392507283" y1="54" x2="195.7151392507283" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="268.40954341048007" cy="51" r="7" fill="transparent"></circle> <circle cx="268.40954341048007" cy="51" r="3"></circle> <text x="268.40954341048007" y="44" text-anchor="middle">0.11</text> <text x="265.40954341048007" y="108" text-anchor="end">some</text> <line x1="268.40954341048007" y1="54" x2="268.40954341048007" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="294.67368950503334" cy="51" r="7" fill="transparent"></circle> <circle cx="294.67368950503334" cy="51" r="3"></circle> <text x="294.67368950503334" y="44" text-anchor="middle">0.15</text> <text x="291.67368950503334" y="120" text-anchor="end">many</text> <line x1="294.67368950503334" y1="54" x2="294.67368950503334" y2="120" stroke-width="1"></line> </g>,<g> <circle cx="372.33333333333326" cy="51" r="7" fill="transparent"></circle> <circle cx="372.33333333333326" cy="51" r="3"></circle> <text x="372.33333333333326" y="44" text-anchor="middle">0.28</text> <text x="369.33333333333326" y="132" text-anchor="end">all</text> <line x1="372.33333333333326" y1="54" x2="372.33333333333326" y2="132" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>none,<wbr>few,<wbr>some,<wbr>many,<wbr>all</code> </td></tr></tbody></table> </figure> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 138"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'frequency' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="44.856740409399734" y1="48" x2="44.856740409399734" y2="54" stroke-width="1"></line> , <line x1="75.90547330013769" y1="48" x2="75.90547330013769" y2="54" stroke-width="1"></line> , <line x1="106.95420619087564" y1="48" x2="106.95420619087564" y2="54" stroke-width="1"></line> , <line x1="138.0029390816136" y1="48" x2="138.0029390816136" y2="54" stroke-width="1"></line> , <line x1="169.05167197235156" y1="48" x2="169.05167197235156" y2="54" stroke-width="1"></line> , <line x1="200.1004048630895" y1="48" x2="200.1004048630895" y2="54" stroke-width="1"></line> , <line x1="231.14913775382746" y1="48" x2="231.14913775382746" y2="54" stroke-width="1"></line> , <line x1="262.19787064456546" y1="48" x2="262.19787064456546" y2="54" stroke-width="1"></line> , <line x1="293.2466035353034" y1="48" x2="293.2466035353034" y2="54" stroke-width="1"></line> , <line x1="324.29533642604133" y1="48" x2="324.29533642604133" y2="54" stroke-width="1"></line> , <line x1="355.3440693167793" y1="48" x2="355.3440693167793" y2="54" stroke-width="1"></line> <line x1="262.19787064456546" y1="48" x2="262.19787064456546" y2="54" stroke-width="1.5"></line> <text x="262.19787064456546" y="45" text-anchor="middle">0</text> <g> <circle cx="55.66666666666666" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666666" cy="51" r="3"></circle> <text x="55.66666666666666" y="44" text-anchor="middle">-0.33</text> <text x="52.66666666666666" y="84" text-anchor="end">never</text> <line x1="55.66666666666666" y1="54" x2="55.66666666666666" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="104.17166073787388" cy="51" r="7" fill="transparent"></circle> <circle cx="104.17166073787388" cy="51" r="3"></circle> <text x="104.17166073787388" y="44" text-anchor="middle">-0.25</text> <text x="101.17166073787388" y="96" text-anchor="end">rarely</text> <line x1="104.17166073787388" y1="54" x2="104.17166073787388" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="337.83320840742203" cy="51" r="7" fill="transparent"></circle> <circle cx="337.83320840742203" cy="51" r="3"></circle> <text x="337.83320840742203" y="44" text-anchor="middle">0.12</text> <text x="334.83320840742203" y="108" text-anchor="end">always</text> <line x1="337.83320840742203" y1="54" x2="337.83320840742203" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="360.2259715687625" cy="51" r="7" fill="transparent"></circle> <circle cx="360.2259715687625" cy="51" r="3"></circle> <text x="360.2259715687625" y="44" text-anchor="middle">0.16</text> <text x="357.2259715687625" y="120" text-anchor="end">often</text> <line x1="360.2259715687625" y1="54" x2="360.2259715687625" y2="120" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.18</text> <text x="369.3333333333333" y="132" text-anchor="end">sometimes</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="132" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>never,<wbr>rarely,<wbr>always,<wbr>often,<wbr>sometimes</code> </td></tr></tbody></table> </figure> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 114"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'frequency' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="63.640064569283716" y1="48" x2="63.640064569283716" y2="54" stroke-width="1"></line> , <line x1="88.34361432210247" y1="48" x2="88.34361432210247" y2="54" stroke-width="1"></line> , <line x1="113.04716407492123" y1="48" x2="113.04716407492123" y2="54" stroke-width="1"></line> , <line x1="137.75071382774" y1="48" x2="137.75071382774" y2="54" stroke-width="1"></line> , <line x1="162.45426358055875" y1="48" x2="162.45426358055875" y2="54" stroke-width="1"></line> , <line x1="187.15781333337756" y1="48" x2="187.15781333337756" y2="54" stroke-width="1"></line> , <line x1="211.86136308619632" y1="48" x2="211.86136308619632" y2="54" stroke-width="1"></line> , <line x1="236.56491283901508" y1="48" x2="236.56491283901508" y2="54" stroke-width="1"></line> , <line x1="261.26846259183384" y1="48" x2="261.26846259183384" y2="54" stroke-width="1"></line> , <line x1="285.97201234465257" y1="48" x2="285.97201234465257" y2="54" stroke-width="1"></line> , <line x1="310.67556209747136" y1="48" x2="310.67556209747136" y2="54" stroke-width="1"></line> , <line x1="335.37911185029014" y1="48" x2="335.37911185029014" y2="54" stroke-width="1"></line> , <line x1="360.0826616031089" y1="48" x2="360.0826616031089" y2="54" stroke-width="1"></line> , <line x1="384.7862113559276" y1="48" x2="384.7862113559276" y2="54" stroke-width="1"></line> <line x1="211.86136308619632" y1="48" x2="211.86136308619632" y2="54" stroke-width="1.5"></line> <text x="211.86136308619632" y="45" text-anchor="middle">0</text> <g> <circle cx="55.66666666666667" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666667" cy="51" r="3"></circle> <text x="55.66666666666667" y="44" text-anchor="middle">-0.32</text> <text x="52.66666666666667" y="84" text-anchor="end">rare</text> <line x1="55.66666666666667" y1="54" x2="55.66666666666667" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="199.80201639830887" cy="51" r="7" fill="transparent"></circle> <circle cx="199.80201639830887" cy="51" r="3"></circle> <text x="199.80201639830887" y="44" text-anchor="middle">-0.02</text> <text x="196.80201639830887" y="96" text-anchor="end">uncommon</text> <line x1="199.80201639830887" y1="54" x2="199.80201639830887" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.32</text> <text x="369.3333333333333" y="108" text-anchor="end">common</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="108" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>rare,<wbr>uncommon,<wbr>common</code> </td></tr></tbody></table> </figure> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 126"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'frequency' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="54.45005067242404" y1="48" x2="54.45005067242404" y2="54" stroke-width="1"></line> , <line x1="70.04905413632912" y1="48" x2="70.04905413632912" y2="54" stroke-width="1"></line> , <line x1="85.64805760023421" y1="48" x2="85.64805760023421" y2="54" stroke-width="1"></line> , <line x1="101.2470610641393" y1="48" x2="101.2470610641393" y2="54" stroke-width="1"></line> , <line x1="116.84606452804438" y1="48" x2="116.84606452804438" y2="54" stroke-width="1"></line> , <line x1="132.44506799194946" y1="48" x2="132.44506799194946" y2="54" stroke-width="1"></line> , <line x1="148.04407145585452" y1="48" x2="148.04407145585452" y2="54" stroke-width="1"></line> , <line x1="163.64307491975958" y1="48" x2="163.64307491975958" y2="54" stroke-width="1"></line> , <line x1="179.24207838366462" y1="48" x2="179.24207838366462" y2="54" stroke-width="1"></line> , <line x1="194.84108184756968" y1="48" x2="194.84108184756968" y2="54" stroke-width="1"></line> , <line x1="210.44008531147477" y1="48" x2="210.44008531147477" y2="54" stroke-width="1"></line> , <line x1="226.03908877537984" y1="48" x2="226.03908877537984" y2="54" stroke-width="1"></line> , <line x1="241.6380922392849" y1="48" x2="241.6380922392849" y2="54" stroke-width="1"></line> , <line x1="257.23709570319" y1="48" x2="257.23709570319" y2="54" stroke-width="1"></line> , <line x1="272.8360991670951" y1="48" x2="272.8360991670951" y2="54" stroke-width="1"></line> , <line x1="288.4351026310002" y1="48" x2="288.4351026310002" y2="54" stroke-width="1"></line> , <line x1="304.0341060949052" y1="48" x2="304.0341060949052" y2="54" stroke-width="1"></line> , <line x1="319.6331095588103" y1="48" x2="319.6331095588103" y2="54" stroke-width="1"></line> , <line x1="335.23211302271534" y1="48" x2="335.23211302271534" y2="54" stroke-width="1"></line> , <line x1="350.8311164866204" y1="48" x2="350.8311164866204" y2="54" stroke-width="1"></line> , <line x1="366.43011995052547" y1="48" x2="366.43011995052547" y2="54" stroke-width="1"></line> , <line x1="382.02912341443056" y1="48" x2="382.02912341443056" y2="54" stroke-width="1"></line> <line x1="319.6331095588103" y1="48" x2="319.6331095588103" y2="54" stroke-width="1.5"></line> <text x="319.6331095588103" y="45" text-anchor="middle">0</text> <g> <circle cx="55.66666666666666" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666666" cy="51" r="3"></circle> <text x="55.66666666666666" y="44" text-anchor="middle">-0.34</text> <text x="52.66666666666666" y="84" text-anchor="end">unlikely</text> <line x1="55.66666666666666" y1="54" x2="55.66666666666666" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="60.22792081946747" cy="51" r="7" fill="transparent"></circle> <circle cx="60.22792081946747" cy="51" r="3"></circle> <text x="60.22792081946747" y="44" text-anchor="middle">-0.33</text> <text x="57.22792081946747" y="96" text-anchor="end">never</text> <line x1="60.22792081946747" y1="54" x2="60.22792081946747" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="342.3421931370392" cy="51" r="7" fill="transparent"></circle> <circle cx="342.3421931370392" cy="51" r="3"></circle> <text x="342.3421931370392" y="44" text-anchor="middle">0.03</text> <text x="339.3421931370392" y="108" text-anchor="end">likely</text> <line x1="342.3421931370392" y1="54" x2="342.3421931370392" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="372.33333333333337" cy="51" r="7" fill="transparent"></circle> <circle cx="372.33333333333337" cy="51" r="3"></circle> <text x="372.33333333333337" y="44" text-anchor="middle">0.07</text> <text x="369.33333333333337" y="120" text-anchor="end">definitely</text> <line x1="372.33333333333337" y1="54" x2="372.33333333333337" y2="120" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>unlikely,<wbr>never,<wbr>likely,<wbr>definitely</code> </td></tr></tbody></table> </figure> <h2>Limitations</h2> <p>Now, let’s look at the obvious failure cases:</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 162"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'sequence' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="53.63778449255443" y1="48" x2="53.63778449255443" y2="54" stroke-width="1"></line> , <line x1="68.45667673883165" y1="48" x2="68.45667673883165" y2="54" stroke-width="1"></line> , <line x1="83.27556898510886" y1="48" x2="83.27556898510886" y2="54" stroke-width="1"></line> , <line x1="98.09446123138608" y1="48" x2="98.09446123138608" y2="54" stroke-width="1"></line> , <line x1="112.9133534776633" y1="48" x2="112.9133534776633" y2="54" stroke-width="1"></line> , <line x1="127.73224572394052" y1="48" x2="127.73224572394052" y2="54" stroke-width="1"></line> , <line x1="142.55113797021772" y1="48" x2="142.55113797021772" y2="54" stroke-width="1"></line> , <line x1="157.37003021649494" y1="48" x2="157.37003021649494" y2="54" stroke-width="1"></line> , <line x1="172.18892246277215" y1="48" x2="172.18892246277215" y2="54" stroke-width="1"></line> , <line x1="187.00781470904934" y1="48" x2="187.00781470904934" y2="54" stroke-width="1"></line> , <line x1="201.82670695532656" y1="48" x2="201.82670695532656" y2="54" stroke-width="1"></line> , <line x1="216.64559920160377" y1="48" x2="216.64559920160377" y2="54" stroke-width="1"></line> , <line x1="231.46449144788102" y1="48" x2="231.46449144788102" y2="54" stroke-width="1"></line> , <line x1="246.28338369415823" y1="48" x2="246.28338369415823" y2="54" stroke-width="1"></line> , <line x1="261.10227594043545" y1="48" x2="261.10227594043545" y2="54" stroke-width="1"></line> , <line x1="275.92116818671263" y1="48" x2="275.92116818671263" y2="54" stroke-width="1"></line> , <line x1="290.7400604329899" y1="48" x2="290.7400604329899" y2="54" stroke-width="1"></line> , <line x1="305.5589526792671" y1="48" x2="305.5589526792671" y2="54" stroke-width="1"></line> , <line x1="320.37784492554437" y1="48" x2="320.37784492554437" y2="54" stroke-width="1"></line> , <line x1="335.1967371718216" y1="48" x2="335.1967371718216" y2="54" stroke-width="1"></line> , <line x1="350.01562941809885" y1="48" x2="350.01562941809885" y2="54" stroke-width="1"></line> , <line x1="364.83452166437604" y1="48" x2="364.83452166437604" y2="54" stroke-width="1"></line> , <line x1="379.6534139106533" y1="48" x2="379.6534139106533" y2="54" stroke-width="1"></line> <g> <circle cx="79.11265779613598" cy="51" r="7" fill="transparent"></circle> <circle cx="79.11265779613598" cy="51" r="3"></circle> <text x="79.11265779613598" y="44" text-anchor="middle">0.02</text> <text x="76.11265779613598" y="84" text-anchor="end">monday</text> <line x1="79.11265779613598" y1="54" x2="79.11265779613598" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="79.73104317683298" cy="51" r="7" fill="transparent"></circle> <circle cx="79.73104317683298" cy="51" r="3"></circle> <text x="79.73104317683298" y="44" text-anchor="middle">0.02</text> <text x="76.73104317683298" y="96" text-anchor="end">wednesday</text> <line x1="79.73104317683298" y1="54" x2="79.73104317683298" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="82.45484852348551" cy="51" r="7" fill="transparent"></circle> <circle cx="82.45484852348551" cy="51" r="3"></circle> <text x="82.45484852348551" y="44" text-anchor="middle">0.02</text> <text x="79.45484852348551" y="108" text-anchor="end">thursday</text> <line x1="82.45484852348551" y1="54" x2="82.45484852348551" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="137.10160994118087" cy="51" r="7" fill="transparent"></circle> <circle cx="137.10160994118087" cy="51" r="3"></circle> <text x="137.10160994118087" y="44" text-anchor="middle">0.04</text> <text x="134.10160994118087" y="120" text-anchor="end">tuesday</text> <line x1="137.10160994118087" y1="54" x2="137.10160994118087" y2="120" stroke-width="1"></line> </g>,<g> <circle cx="149.17814640779434" cy="51" r="7" fill="transparent"></circle> <circle cx="149.17814640779434" cy="51" r="3"></circle> <text x="149.17814640779434" y="44" text-anchor="middle">0.04</text> <text x="146.17814640779434" y="132" text-anchor="end">friday</text> <line x1="149.17814640779434" y1="54" x2="149.17814640779434" y2="132" stroke-width="1"></line> </g>,<g> <circle cx="297.2438394858473" cy="51" r="7" fill="transparent"></circle> <circle cx="297.2438394858473" cy="51" r="3"></circle> <text x="297.2438394858473" y="44" text-anchor="middle">0.09</text> <text x="294.2438394858473" y="144" text-anchor="end">saturday</text> <line x1="297.2438394858473" y1="54" x2="297.2438394858473" y2="144" stroke-width="1"></line> </g>,<g> <circle cx="374.46478707237594" cy="51" r="7" fill="transparent"></circle> <circle cx="374.46478707237594" cy="51" r="3"></circle> <text x="374.46478707237594" y="44" text-anchor="middle">0.12</text> <text x="371.46478707237594" y="156" text-anchor="end">sunday</text> <line x1="374.46478707237594" y1="54" x2="374.46478707237594" y2="156" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>monday,<wbr>wednesday,<wbr>thursday,<wbr>tuesday,<wbr>friday,<wbr>saturday,<wbr>sunday</code> </td></tr></tbody></table> </figure> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 222"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'frequency' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="51.09670023443515" y1="48" x2="51.09670023443515" y2="54" stroke-width="1"></line> , <line x1="73.79527664400288" y1="48" x2="73.79527664400288" y2="54" stroke-width="1"></line> , <line x1="96.49385305357066" y1="48" x2="96.49385305357066" y2="54" stroke-width="1"></line> , <line x1="119.19242946313844" y1="48" x2="119.19242946313844" y2="54" stroke-width="1"></line> , <line x1="141.89100587270622" y1="48" x2="141.89100587270622" y2="54" stroke-width="1"></line> , <line x1="164.58958228227397" y1="48" x2="164.58958228227397" y2="54" stroke-width="1"></line> , <line x1="187.28815869184172" y1="48" x2="187.28815869184172" y2="54" stroke-width="1"></line> , <line x1="209.9867351014095" y1="48" x2="209.9867351014095" y2="54" stroke-width="1"></line> , <line x1="232.68531151097727" y1="48" x2="232.68531151097727" y2="54" stroke-width="1"></line> , <line x1="255.38388792054502" y1="48" x2="255.38388792054502" y2="54" stroke-width="1"></line> , <line x1="278.08246433011277" y1="48" x2="278.08246433011277" y2="54" stroke-width="1"></line> , <line x1="300.7810407396806" y1="48" x2="300.7810407396806" y2="54" stroke-width="1"></line> , <line x1="323.4796171492483" y1="48" x2="323.4796171492483" y2="54" stroke-width="1"></line> , <line x1="346.1781935588161" y1="48" x2="346.1781935588161" y2="54" stroke-width="1"></line> , <line x1="368.8767699683838" y1="48" x2="368.8767699683838" y2="54" stroke-width="1"></line> <line x1="232.68531151097727" y1="48" x2="232.68531151097727" y2="54" stroke-width="1.5"></line> <text x="232.68531151097727" y="45" text-anchor="middle">0</text> <g> <circle cx="55.666666666666664" cy="51" r="7" fill="transparent"></circle> <circle cx="55.666666666666664" cy="51" r="3"></circle> <text x="55.666666666666664" y="44" text-anchor="middle">-0.08</text> <text x="52.666666666666664" y="84" text-anchor="end">april</text> <line x1="55.666666666666664" y1="54" x2="55.666666666666664" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="81.02633305213942" cy="51" r="7" fill="transparent"></circle> <circle cx="81.02633305213942" cy="51" r="3"></circle> <text x="81.02633305213942" y="44" text-anchor="middle">-0.07</text> <text x="78.02633305213942" y="96" text-anchor="end">june</text> <line x1="81.02633305213942" y1="54" x2="81.02633305213942" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="86.27316708087227" cy="51" r="7" fill="transparent"></circle> <circle cx="86.27316708087227" cy="51" r="3"></circle> <text x="86.27316708087227" y="44" text-anchor="middle">-0.06</text> <text x="83.27316708087227" y="108" text-anchor="end">march</text> <line x1="86.27316708087227" y1="54" x2="86.27316708087227" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="88.44366923256581" cy="51" r="7" fill="transparent"></circle> <circle cx="88.44366923256581" cy="51" r="3"></circle> <text x="88.44366923256581" y="44" text-anchor="middle">-0.06</text> <text x="85.44366923256581" y="120" text-anchor="end">january</text> <line x1="88.44366923256581" y1="54" x2="88.44366923256581" y2="120" stroke-width="1"></line> </g>,<g> <circle cx="92.83314002104783" cy="51" r="7" fill="transparent"></circle> <circle cx="92.83314002104783" cy="51" r="3"></circle> <text x="92.83314002104783" y="44" text-anchor="middle">-0.06</text> <text x="89.83314002104783" y="132" text-anchor="end">november</text> <line x1="92.83314002104783" y1="54" x2="92.83314002104783" y2="132" stroke-width="1"></line> </g>,<g> <circle cx="104.23195154776292" cy="51" r="7" fill="transparent"></circle> <circle cx="104.23195154776292" cy="51" r="3"></circle> <text x="104.23195154776292" y="44" text-anchor="middle">-0.06</text> <text x="101.23195154776292" y="144" text-anchor="end">february</text> <line x1="104.23195154776292" y1="54" x2="104.23195154776292" y2="144" stroke-width="1"></line> </g>,<g> <circle cx="137.32880360198385" cy="51" r="7" fill="transparent"></circle> <circle cx="137.32880360198385" cy="51" r="3"></circle> <text x="137.32880360198385" y="44" text-anchor="middle">-0.04</text> <text x="134.32880360198385" y="156" text-anchor="end">july</text> <line x1="137.32880360198385" y1="54" x2="137.32880360198385" y2="156" stroke-width="1"></line> </g>,<g> <circle cx="141.0168413467661" cy="51" r="7" fill="transparent"></circle> <circle cx="141.0168413467661" cy="51" r="3"></circle> <text x="141.0168413467661" y="44" text-anchor="middle">-0.04</text> <text x="138.0168413467661" y="168" text-anchor="end">october</text> <line x1="141.0168413467661" y1="54" x2="141.0168413467661" y2="168" stroke-width="1"></line> </g>,<g> <circle cx="151.51874610841054" cy="51" r="7" fill="transparent"></circle> <circle cx="151.51874610841054" cy="51" r="3"></circle> <text x="151.51874610841054" y="44" text-anchor="middle">-0.04</text> <text x="148.51874610841054" y="180" text-anchor="end">august</text> <line x1="151.51874610841054" y1="54" x2="151.51874610841054" y2="180" stroke-width="1"></line> </g>,<g> <circle cx="195.99570137849625" cy="51" r="7" fill="transparent"></circle> <circle cx="195.99570137849625" cy="51" r="3"></circle> <text x="195.99570137849625" y="44" text-anchor="middle">-0.02</text> <text x="192.99570137849625" y="192" text-anchor="end">december</text> <line x1="195.99570137849625" y1="54" x2="195.99570137849625" y2="192" stroke-width="1"></line> </g>,<g> <circle cx="198.95175919837575" cy="51" r="7" fill="transparent"></circle> <circle cx="198.95175919837575" cy="51" r="3"></circle> <text x="198.95175919837575" y="44" text-anchor="middle">-0.01</text> <text x="195.95175919837575" y="204" text-anchor="end">september</text> <line x1="198.95175919837575" y1="54" x2="198.95175919837575" y2="204" stroke-width="1"></line> </g>,<g> <circle cx="372.33333333333337" cy="51" r="7" fill="transparent"></circle> <circle cx="372.33333333333337" cy="51" r="3"></circle> <text x="372.33333333333337" y="44" text-anchor="middle">0.06</text> <text x="369.33333333333337" y="216" text-anchor="end">may</text> <line x1="372.33333333333337" y1="54" x2="372.33333333333337" y2="216" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>april,<wbr>june,<wbr>march,<wbr>january,<wbr>november,<wbr>february,<wbr>july,<wbr>october,<wbr>august,<wbr>december,<wbr>september,<wbr>may</code> </td></tr></tbody></table> </figure> <p>Days of the week and months have obvious sequences <small>(or not, is it Sunday or Monday for the start of the week?)</small> but these linear projections don’t quite capture it.</p> <p>Let’s look at the 2D projections:</p> <figure> <svg viewBox="0 0 400 400"><defs> <marker id="scatter-arrow-end" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> <marker id="scatter-arrow-start" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> </defs> <line x1="33.6" y1="200" x2="366.4" y2="200" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="200" y1="309.4" x2="200" y2="90.6" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="76.1" y1="197" x2="76.1" y2="203" stroke-width="1"></line><line x1="101.2" y1="197" x2="101.2" y2="203" stroke-width="1"></line><line x1="126.3" y1="197" x2="126.3" y2="203" stroke-width="1"></line><line x1="151.3" y1="197" x2="151.3" y2="203" stroke-width="1"></line><line x1="176.4" y1="197" x2="176.4" y2="203" stroke-width="1"></line><line x1="201.5" y1="197" x2="201.5" y2="203" stroke-width="1"></line><line x1="226.6" y1="197" x2="226.6" y2="203" stroke-width="1"></line><line x1="251.6" y1="197" x2="251.6" y2="203" stroke-width="1"></line><line x1="276.7" y1="197" x2="276.7" y2="203" stroke-width="1"></line><line x1="301.8" y1="197" x2="301.8" y2="203" stroke-width="1"></line><line x1="326.9" y1="197" x2="326.9" y2="203" stroke-width="1"></line><line x1="197" y1="267.9" x2="203" y2="267.9" stroke-width="1"></line><line x1="197" y1="242.8" x2="203" y2="242.8" stroke-width="1"></line><line x1="197" y1="217.7" x2="203" y2="217.7" stroke-width="1"></line><line x1="197" y1="192.6" x2="203" y2="192.6" stroke-width="1"></line><line x1="197" y1="167.5" x2="203" y2="167.5" stroke-width="1"></line><line x1="197" y1="142.5" x2="203" y2="142.5" stroke-width="1"></line><line x1="197" y1="117.4" x2="203" y2="117.4" stroke-width="1"></line><g> <circle cx="340.6" cy="218.7" r="7" fill="transparent"></circle> <circle cx="340.6" cy="218.7" r="3"></circle> <text x="333.6" y="222.7" text-anchor="end">monday</text> <text x="333.6" y="232.7" text-anchor="end">(0.18, -0.05)</text> </g>,<g> <circle cx="352.0" cy="204.8" r="7" fill="transparent"></circle> <circle cx="352.0" cy="204.8" r="3"></circle> <text x="345.0" y="208.8" text-anchor="end">tuesday</text> <text x="345.0" y="218.8" text-anchor="end">(0.20, -0.02)</text> </g>,<g> <circle cx="350.6" cy="207.4" r="7" fill="transparent"></circle> <circle cx="350.6" cy="207.4" r="3"></circle> <text x="343.6" y="211.4" text-anchor="end">wednesday</text> <text x="343.6" y="221.4" text-anchor="end">(0.20, -0.03)</text> </g>,<g> <circle cx="343.9" cy="189.5" r="7" fill="transparent"></circle> <circle cx="343.9" cy="189.5" r="3"></circle> <text x="336.9" y="193.5" text-anchor="end">thursday</text> <text x="336.9" y="203.5" text-anchor="end">(0.18, 0.01)</text> </g>,<g> <circle cx="278.3" cy="127.9" r="7" fill="transparent"></circle> <circle cx="278.3" cy="127.9" r="3"></circle> <text x="271.3" y="131.9" text-anchor="end">friday</text> <text x="271.3" y="141.9" text-anchor="end">(0.05, 0.13)</text> </g>,<g> <circle cx="48.0" cy="105.0" r="7" fill="transparent"></circle> <circle cx="48.0" cy="105.0" r="3"></circle> <text x="55.0" y="109.0" text-anchor="start">saturday</text> <text x="55.0" y="119.0" text-anchor="start">(-0.41, 0.17)</text> </g>,<g> <circle cx="48.1" cy="295.0" r="7" fill="transparent"></circle> <circle cx="48.1" cy="295.0" r="3"></circle> <text x="55.1" y="299.0" text-anchor="start">sunday</text> <text x="55.1" y="309.0" text-anchor="start">(-0.41, -0.20)</text> </g></svg> <figcaption>The days are clustered more strongly by weekday and weekend.</figcaption> </figure> <p>The months reveal another limitation:</p> <figure> <svg viewBox="0 0 400 400"><defs> <marker id="scatter-arrow-end" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> <marker id="scatter-arrow-start" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> </defs> <line x1="33.6" y1="200" x2="366.4" y2="200" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="200" y1="267.1" x2="200" y2="132.9" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="79.1" y1="197" x2="79.1" y2="203" stroke-width="1"></line><line x1="101.3" y1="197" x2="101.3" y2="203" stroke-width="1"></line><line x1="123.4" y1="197" x2="123.4" y2="203" stroke-width="1"></line><line x1="145.5" y1="197" x2="145.5" y2="203" stroke-width="1"></line><line x1="167.6" y1="197" x2="167.6" y2="203" stroke-width="1"></line><line x1="189.7" y1="197" x2="189.7" y2="203" stroke-width="1"></line><line x1="211.9" y1="197" x2="211.9" y2="203" stroke-width="1"></line><line x1="234.0" y1="197" x2="234.0" y2="203" stroke-width="1"></line><line x1="256.1" y1="197" x2="256.1" y2="203" stroke-width="1"></line><line x1="278.2" y1="197" x2="278.2" y2="203" stroke-width="1"></line><line x1="300.4" y1="197" x2="300.4" y2="203" stroke-width="1"></line><line x1="322.5" y1="197" x2="322.5" y2="203" stroke-width="1"></line><line x1="197" y1="239.6" x2="203" y2="239.6" stroke-width="1"></line><line x1="197" y1="228.6" x2="203" y2="228.6" stroke-width="1"></line><line x1="197" y1="217.5" x2="203" y2="217.5" stroke-width="1"></line><line x1="197" y1="206.4" x2="203" y2="206.4" stroke-width="1"></line><line x1="197" y1="195.4" x2="203" y2="195.4" stroke-width="1"></line><line x1="197" y1="184.3" x2="203" y2="184.3" stroke-width="1"></line><line x1="197" y1="173.3" x2="203" y2="173.3" stroke-width="1"></line><line x1="197" y1="162.2" x2="203" y2="162.2" stroke-width="1"></line><g> <circle cx="345.4" cy="152.4" r="7" fill="transparent"></circle> <circle cx="345.4" cy="152.4" r="3"></circle> <text x="338.4" y="156.4" text-anchor="end">january</text> <text x="338.4" y="166.4" text-anchor="end">(0.10, 0.19)</text> </g>,<g> <circle cx="350.0" cy="166.4" r="7" fill="transparent"></circle> <circle cx="350.0" cy="166.4" r="3"></circle> <text x="343.0" y="170.4" text-anchor="end">february</text> <text x="343.0" y="180.4" text-anchor="end">(0.12, 0.13)</text> </g>,<g> <circle cx="349.8" cy="225.0" r="7" fill="transparent"></circle> <circle cx="349.8" cy="225.0" r="3"></circle> <text x="342.8" y="229.0" text-anchor="end">march</text> <text x="342.8" y="239.0" text-anchor="end">(0.12, -0.13)</text> </g>,<g> <circle cx="344.5" cy="246.4" r="7" fill="transparent"></circle> <circle cx="344.5" cy="246.4" r="3"></circle> <text x="337.5" y="250.4" text-anchor="end">april</text> <text x="337.5" y="260.4" text-anchor="end">(0.10, -0.23)</text> </g>,<g> <circle cx="48.0" cy="195.2" r="7" fill="transparent"></circle> <circle cx="48.0" cy="195.2" r="3"></circle> <text x="55.0" y="199.2" text-anchor="start">may</text> <text x="55.0" y="209.2" text-anchor="start">(-1.24, 0.00)</text> </g>,<g> <circle cx="347.0" cy="246.7" r="7" fill="transparent"></circle> <circle cx="347.0" cy="246.7" r="3"></circle> <text x="340.0" y="250.7" text-anchor="end">june</text> <text x="340.0" y="260.7" text-anchor="end">(0.11, -0.23)</text> </g>,<g> <circle cx="344.7" cy="252.7" r="7" fill="transparent"></circle> <circle cx="344.7" cy="252.7" r="3"></circle> <text x="337.7" y="256.7" text-anchor="end">july</text> <text x="337.7" y="266.7" text-anchor="end">(0.10, -0.26)</text> </g>,<g> <circle cx="351.5" cy="197.0" r="7" fill="transparent"></circle> <circle cx="351.5" cy="197.0" r="3"></circle> <text x="344.5" y="201.0" text-anchor="end">august</text> <text x="344.5" y="211.0" text-anchor="end">(0.13, -0.01)</text> </g>,<g> <circle cx="349.4" cy="185.3" r="7" fill="transparent"></circle> <circle cx="349.4" cy="185.3" r="3"></circle> <text x="342.4" y="189.3" text-anchor="end">september</text> <text x="342.4" y="199.3" text-anchor="end">(0.12, 0.05)</text> </g>,<g> <circle cx="352.0" cy="174.9" r="7" fill="transparent"></circle> <circle cx="352.0" cy="174.9" r="3"></circle> <text x="345.0" y="178.9" text-anchor="end">october</text> <text x="345.0" y="188.9" text-anchor="end">(0.13, 0.09)</text> </g>,<g> <circle cx="340.0" cy="155.2" r="7" fill="transparent"></circle> <circle cx="340.0" cy="155.2" r="3"></circle> <text x="333.0" y="159.2" text-anchor="end">november</text> <text x="333.0" y="169.2" text-anchor="end">(0.08, 0.18)</text> </g>,<g> <circle cx="347.5" cy="147.3" r="7" fill="transparent"></circle> <circle cx="347.5" cy="147.3" r="3"></circle> <text x="340.5" y="151.3" text-anchor="end">december</text> <text x="340.5" y="161.3" text-anchor="end">(0.11, 0.22)</text> </g></svg> <figcaption>May is an outlier.</figcaption> </figure> <p>The word <code>"may"</code> can mean different things. Word embeddings alone cannot capture the specific sense of the word within the context of the rest of the input words. Additional processing should be done to disambiguate the 5th month of the year <code>"may"</code> from the expression of possibility <code>"may"</code>.</p> <p>While the solution works in principle, there are many blind spots and edge cases to worry about. I explored a few more variants.</p> <h2>Alternatives considered</h2> <p>What if we just automatically fit a line through the words, and use that as the order vector? This eliminates the bias of human-curated anchor words.</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 138"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> PC1 </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="60.22151601623999" y1="48" x2="60.22151601623999" y2="54" stroke-width="1"></line> , <line x1="86.60559508859366" y1="48" x2="86.60559508859366" y2="54" stroke-width="1"></line> , <line x1="112.98967416094735" y1="48" x2="112.98967416094735" y2="54" stroke-width="1"></line> , <line x1="139.37375323330102" y1="48" x2="139.37375323330102" y2="54" stroke-width="1"></line> , <line x1="165.7578323056547" y1="48" x2="165.7578323056547" y2="54" stroke-width="1"></line> , <line x1="192.14191137800836" y1="48" x2="192.14191137800836" y2="54" stroke-width="1"></line> , <line x1="218.52599045036203" y1="48" x2="218.52599045036203" y2="54" stroke-width="1"></line> , <line x1="244.91006952271573" y1="48" x2="244.91006952271573" y2="54" stroke-width="1"></line> , <line x1="271.2941485950694" y1="48" x2="271.2941485950694" y2="54" stroke-width="1"></line> , <line x1="297.6782276674231" y1="48" x2="297.6782276674231" y2="54" stroke-width="1"></line> , <line x1="324.0623067397768" y1="48" x2="324.0623067397768" y2="54" stroke-width="1"></line> , <line x1="350.4463858121305" y1="48" x2="350.4463858121305" y2="54" stroke-width="1"></line> , <line x1="376.8304648844842" y1="48" x2="376.8304648844842" y2="54" stroke-width="1"></line> <line x1="244.9100695227158" y1="48" x2="244.9100695227158" y2="54" stroke-width="1.5"></line> <text x="244.9100695227158" y="45" text-anchor="middle">0</text> <g> <circle cx="55.666666666666664" cy="51" r="7" fill="transparent"></circle> <circle cx="55.666666666666664" cy="51" r="3"></circle> <text x="55.666666666666664" y="44" text-anchor="middle">-0.72</text> <text x="52.666666666666664" y="84" text-anchor="end">medium</text> <line x1="55.666666666666664" y1="54" x2="55.666666666666664" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="317.08835906023364" cy="51" r="7" fill="transparent"></circle> <circle cx="317.08835906023364" cy="51" r="3"></circle> <text x="317.08835906023364" y="44" text-anchor="middle">0.27</text> <text x="314.08835906023364" y="96" text-anchor="end">small</text> <line x1="317.08835906023364" y1="54" x2="317.08835906023364" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="347.63241741237084" cy="51" r="7" fill="transparent"></circle> <circle cx="347.63241741237084" cy="51" r="3"></circle> <text x="347.63241741237084" y="44" text-anchor="middle">0.39</text> <text x="344.63241741237084" y="108" text-anchor="end">large</text> <line x1="347.63241741237084" y1="54" x2="347.63241741237084" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="363.8011528154593" cy="51" r="7" fill="transparent"></circle> <circle cx="363.8011528154593" cy="51" r="3"></circle> <text x="363.8011528154593" y="44" text-anchor="middle">0.45</text> <text x="360.8011528154593" y="120" text-anchor="end">tiny</text> <line x1="363.8011528154593" y1="54" x2="363.8011528154593" y2="120" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.48</text> <text x="369.3333333333333" y="132" text-anchor="end">huge</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="132" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>medium,<wbr>small,<wbr>large,<wbr>tiny,<wbr>huge</code> </td></tr></tbody></table> </figure> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 126"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> PC1 </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="49.980469675235796" y1="48" x2="49.980469675235796" y2="54" stroke-width="1"></line> , <line x1="64.0849861227668" y1="48" x2="64.0849861227668" y2="54" stroke-width="1"></line> , <line x1="78.18950257029779" y1="48" x2="78.18950257029779" y2="54" stroke-width="1"></line> , <line x1="92.2940190178288" y1="48" x2="92.2940190178288" y2="54" stroke-width="1"></line> , <line x1="106.3985354653598" y1="48" x2="106.3985354653598" y2="54" stroke-width="1"></line> , <line x1="120.5030519128908" y1="48" x2="120.5030519128908" y2="54" stroke-width="1"></line> , <line x1="134.6075683604218" y1="48" x2="134.6075683604218" y2="54" stroke-width="1"></line> , <line x1="148.7120848079528" y1="48" x2="148.7120848079528" y2="54" stroke-width="1"></line> , <line x1="162.81660125548382" y1="48" x2="162.81660125548382" y2="54" stroke-width="1"></line> , <line x1="176.9211177030148" y1="48" x2="176.9211177030148" y2="54" stroke-width="1"></line> , <line x1="191.0256341505458" y1="48" x2="191.0256341505458" y2="54" stroke-width="1"></line> , <line x1="205.13015059807682" y1="48" x2="205.13015059807682" y2="54" stroke-width="1"></line> , <line x1="219.2346670456078" y1="48" x2="219.2346670456078" y2="54" stroke-width="1"></line> , <line x1="233.33918349313882" y1="48" x2="233.33918349313882" y2="54" stroke-width="1"></line> , <line x1="247.44369994066986" y1="48" x2="247.44369994066986" y2="54" stroke-width="1"></line> , <line x1="261.54821638820084" y1="48" x2="261.54821638820084" y2="54" stroke-width="1"></line> , <line x1="275.6527328357318" y1="48" x2="275.6527328357318" y2="54" stroke-width="1"></line> , <line x1="289.7572492832628" y1="48" x2="289.7572492832628" y2="54" stroke-width="1"></line> , <line x1="303.8617657307939" y1="48" x2="303.8617657307939" y2="54" stroke-width="1"></line> , <line x1="317.9662821783249" y1="48" x2="317.9662821783249" y2="54" stroke-width="1"></line> , <line x1="332.0707986258558" y1="48" x2="332.0707986258558" y2="54" stroke-width="1"></line> , <line x1="346.1753150733869" y1="48" x2="346.1753150733869" y2="54" stroke-width="1"></line> , <line x1="360.27983152091787" y1="48" x2="360.27983152091787" y2="54" stroke-width="1"></line> , <line x1="374.38434796844894" y1="48" x2="374.38434796844894" y2="54" stroke-width="1"></line> <line x1="176.92111770301483" y1="48" x2="176.92111770301483" y2="54" stroke-width="1.5"></line> <text x="176.92111770301483" y="45" text-anchor="middle">0</text> <g> <circle cx="55.66666666666667" cy="51" r="7" fill="transparent"></circle> <circle cx="55.66666666666667" cy="51" r="3"></circle> <text x="55.66666666666667" y="44" text-anchor="middle">-0.43</text> <text x="52.66666666666667" y="84" text-anchor="end">poor</text> <line x1="55.66666666666667" y1="54" x2="55.66666666666667" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="70.05764984017111" cy="51" r="7" fill="transparent"></circle> <circle cx="70.05764984017111" cy="51" r="3"></circle> <text x="70.05764984017111" y="44" text-anchor="middle">-0.38</text> <text x="67.05764984017111" y="96" text-anchor="end">excellent</text> <line x1="70.05764984017111" y1="54" x2="70.05764984017111" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="121.29220162550239" cy="51" r="7" fill="transparent"></circle> <circle cx="121.29220162550239" cy="51" r="3"></circle> <text x="121.29220162550239" y="44" text-anchor="middle">-0.20</text> <text x="118.29220162550239" y="108" text-anchor="end">good</text> <line x1="121.29220162550239" y1="54" x2="121.29220162550239" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.69</text> <text x="369.3333333333333" y="120" text-anchor="end">fair</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="120" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>poor,<wbr>excellent,<wbr>good,<wbr>fair</code> </td></tr></tbody></table> </figure> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 138"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> PC1 </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="57.95417970455203" y1="48" x2="57.95417970455203" y2="54" stroke-width="1"></line> , <line x1="73.36574797490456" y1="48" x2="73.36574797490456" y2="54" stroke-width="1"></line> , <line x1="88.77731624525708" y1="48" x2="88.77731624525708" y2="54" stroke-width="1"></line> , <line x1="104.18888451560962" y1="48" x2="104.18888451560962" y2="54" stroke-width="1"></line> , <line x1="119.60045278596212" y1="48" x2="119.60045278596212" y2="54" stroke-width="1"></line> , <line x1="135.01202105631467" y1="48" x2="135.01202105631467" y2="54" stroke-width="1"></line> , <line x1="150.42358932666718" y1="48" x2="150.42358932666718" y2="54" stroke-width="1"></line> , <line x1="165.8351575970197" y1="48" x2="165.8351575970197" y2="54" stroke-width="1"></line> , <line x1="181.24672586737222" y1="48" x2="181.24672586737222" y2="54" stroke-width="1"></line> , <line x1="196.65829413772477" y1="48" x2="196.65829413772477" y2="54" stroke-width="1"></line> , <line x1="212.06986240807726" y1="48" x2="212.06986240807726" y2="54" stroke-width="1"></line> , <line x1="227.4814306784298" y1="48" x2="227.4814306784298" y2="54" stroke-width="1"></line> , <line x1="242.89299894878232" y1="48" x2="242.89299894878232" y2="54" stroke-width="1"></line> , <line x1="258.30456721913487" y1="48" x2="258.30456721913487" y2="54" stroke-width="1"></line> , <line x1="273.7161354894874" y1="48" x2="273.7161354894874" y2="54" stroke-width="1"></line> , <line x1="289.1277037598399" y1="48" x2="289.1277037598399" y2="54" stroke-width="1"></line> , <line x1="304.5392720301925" y1="48" x2="304.5392720301925" y2="54" stroke-width="1"></line> , <line x1="319.95084030054494" y1="48" x2="319.95084030054494" y2="54" stroke-width="1"></line> , <line x1="335.3624085708975" y1="48" x2="335.3624085708975" y2="54" stroke-width="1"></line> , <line x1="350.77397684125003" y1="48" x2="350.77397684125003" y2="54" stroke-width="1"></line> , <line x1="366.1855451116025" y1="48" x2="366.1855451116025" y2="54" stroke-width="1"></line> , <line x1="381.5971133819551" y1="48" x2="381.5971133819551" y2="54" stroke-width="1"></line> <line x1="212.0698624080773" y1="48" x2="212.0698624080773" y2="54" stroke-width="1.5"></line> <text x="212.0698624080773" y="45" text-anchor="middle">0</text> <g> <circle cx="55.666666666666686" cy="51" r="7" fill="transparent"></circle> <circle cx="55.666666666666686" cy="51" r="3"></circle> <text x="55.666666666666686" y="44" text-anchor="middle">-0.51</text> <text x="52.666666666666686" y="84" text-anchor="end">never</text> <line x1="55.666666666666686" y1="54" x2="55.666666666666686" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="135.7236861481145" cy="51" r="7" fill="transparent"></circle> <circle cx="135.7236861481145" cy="51" r="3"></circle> <text x="135.7236861481145" y="44" text-anchor="middle">-0.25</text> <text x="132.7236861481145" y="96" text-anchor="end">always</text> <line x1="135.7236861481145" y1="54" x2="135.7236861481145" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="257.3988716497139" cy="51" r="7" fill="transparent"></circle> <circle cx="257.3988716497139" cy="51" r="3"></circle> <text x="257.3988716497139" y="44" text-anchor="middle">0.15</text> <text x="254.3988716497139" y="108" text-anchor="end">rarely</text> <line x1="257.3988716497139" y1="54" x2="257.3988716497139" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="371.0566812232207" cy="51" r="7" fill="transparent"></circle> <circle cx="371.0566812232207" cy="51" r="3"></circle> <text x="371.0566812232207" y="44" text-anchor="middle">0.52</text> <text x="368.0566812232207" y="120" text-anchor="end">sometimes</text> <line x1="371.0566812232207" y1="54" x2="371.0566812232207" y2="120" stroke-width="1"></line> </g>,<g> <circle cx="372.33333333333337" cy="51" r="7" fill="transparent"></circle> <circle cx="372.33333333333337" cy="51" r="3"></circle> <text x="372.33333333333337" y="44" text-anchor="middle">0.52</text> <text x="369.33333333333337" y="132" text-anchor="end">often</text> <line x1="372.33333333333337" y1="54" x2="372.33333333333337" y2="132" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>never,<wbr>always,<wbr>rarely,<wbr>sometimes,<wbr>often</code> </td></tr></tbody></table> </figure> <p>The general quality isn’t there yet, but it’s a good option for specific sequences like the ordinals:</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 198"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> PC1 </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="48.563524199119115" y1="48" x2="48.563524199119115" y2="54" stroke-width="1"></line> , <line x1="67.32492263671271" y1="48" x2="67.32492263671271" y2="54" stroke-width="1"></line> , <line x1="86.08632107430631" y1="48" x2="86.08632107430631" y2="54" stroke-width="1"></line> , <line x1="104.84771951189991" y1="48" x2="104.84771951189991" y2="54" stroke-width="1"></line> , <line x1="123.60911794949352" y1="48" x2="123.60911794949352" y2="54" stroke-width="1"></line> , <line x1="142.37051638708712" y1="48" x2="142.37051638708712" y2="54" stroke-width="1"></line> , <line x1="161.1319148246807" y1="48" x2="161.1319148246807" y2="54" stroke-width="1"></line> , <line x1="179.89331326227432" y1="48" x2="179.89331326227432" y2="54" stroke-width="1"></line> , <line x1="198.65471169986793" y1="48" x2="198.65471169986793" y2="54" stroke-width="1"></line> , <line x1="217.41611013746152" y1="48" x2="217.41611013746152" y2="54" stroke-width="1"></line> , <line x1="236.17750857505516" y1="48" x2="236.17750857505516" y2="54" stroke-width="1"></line> , <line x1="254.93890701264874" y1="48" x2="254.93890701264874" y2="54" stroke-width="1"></line> , <line x1="273.70030545024235" y1="48" x2="273.70030545024235" y2="54" stroke-width="1"></line> , <line x1="292.461703887836" y1="48" x2="292.461703887836" y2="54" stroke-width="1"></line> , <line x1="311.2231023254296" y1="48" x2="311.2231023254296" y2="54" stroke-width="1"></line> , <line x1="329.98450076302316" y1="48" x2="329.98450076302316" y2="54" stroke-width="1"></line> , <line x1="348.74589920061675" y1="48" x2="348.74589920061675" y2="54" stroke-width="1"></line> , <line x1="367.5072976382104" y1="48" x2="367.5072976382104" y2="54" stroke-width="1"></line> <line x1="217.41611013746154" y1="48" x2="217.41611013746154" y2="54" stroke-width="1.5"></line> <text x="217.41611013746154" y="45" text-anchor="middle">0</text> <g> <circle cx="55.666666666666664" cy="51" r="7" fill="transparent"></circle> <circle cx="55.666666666666664" cy="51" r="3"></circle> <text x="55.666666666666664" y="44" text-anchor="middle">-0.43</text> <text x="52.666666666666664" y="84" text-anchor="end">first</text> <line x1="55.666666666666664" y1="54" x2="55.666666666666664" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="130.2632136921411" cy="51" r="7" fill="transparent"></circle> <circle cx="130.2632136921411" cy="51" r="3"></circle> <text x="130.2632136921411" y="44" text-anchor="middle">-0.23</text> <text x="127.26321369214111" y="96" text-anchor="end">second</text> <line x1="130.2632136921411" y1="54" x2="130.2632136921411" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="154.8874270815892" cy="51" r="7" fill="transparent"></circle> <circle cx="154.8874270815892" cy="51" r="3"></circle> <text x="154.8874270815892" y="44" text-anchor="middle">-0.17</text> <text x="151.8874270815892" y="108" text-anchor="end">third</text> <line x1="154.8874270815892" y1="54" x2="154.8874270815892" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="180.7944111592833" cy="51" r="7" fill="transparent"></circle> <circle cx="180.7944111592833" cy="51" r="3"></circle> <text x="180.7944111592833" y="44" text-anchor="middle">-0.10</text> <text x="177.7944111592833" y="120" text-anchor="end">fourth</text> <line x1="180.7944111592833" y1="54" x2="180.7944111592833" y2="120" stroke-width="1"></line> </g>,<g> <circle cx="299.80702291502416" cy="51" r="7" fill="transparent"></circle> <circle cx="299.80702291502416" cy="51" r="3"></circle> <text x="299.80702291502416" y="44" text-anchor="middle">0.22</text> <text x="296.80702291502416" y="132" text-anchor="end">fifth</text> <line x1="299.80702291502416" y1="54" x2="299.80702291502416" y2="132" stroke-width="1"></line> </g>,<g> <circle cx="324.5540136588826" cy="51" r="7" fill="transparent"></circle> <circle cx="324.5540136588826" cy="51" r="3"></circle> <text x="324.5540136588826" y="44" text-anchor="middle">0.29</text> <text x="321.5540136588826" y="144" text-anchor="end">tenth</text> <line x1="324.5540136588826" y1="54" x2="324.5540136588826" y2="144" stroke-width="1"></line> </g>,<g> <circle cx="332.58219617215497" cy="51" r="7" fill="transparent"></circle> <circle cx="332.58219617215497" cy="51" r="3"></circle> <text x="332.58219617215497" y="44" text-anchor="middle">0.31</text> <text x="329.58219617215497" y="156" text-anchor="end">sixth</text> <line x1="332.58219617215497" y1="54" x2="332.58219617215497" y2="156" stroke-width="1"></line> </g>,<g> <circle cx="353.76296927265724" cy="51" r="7" fill="transparent"></circle> <circle cx="353.76296927265724" cy="51" r="3"></circle> <text x="353.76296927265724" y="44" text-anchor="middle">0.36</text> <text x="350.76296927265724" y="168" text-anchor="end">seventh</text> <line x1="353.76296927265724" y1="54" x2="353.76296927265724" y2="168" stroke-width="1"></line> </g>,<g> <circle cx="368.8397440976391" cy="51" r="7" fill="transparent"></circle> <circle cx="368.8397440976391" cy="51" r="3"></circle> <text x="368.8397440976391" y="44" text-anchor="middle">0.40</text> <text x="365.8397440976391" y="180" text-anchor="end">eighth</text> <line x1="368.8397440976391" y1="54" x2="368.8397440976391" y2="180" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.41</text> <text x="369.3333333333333" y="192" text-anchor="end">ninth</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="192" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>first,<wbr>second,<wbr>third,<wbr>fourth,<wbr>fifth,<wbr>tenth,<wbr>sixth,<wbr>seventh,<wbr>eighth,<wbr>ninth</code> </td></tr></tbody></table> </figure> <p>I kept adding ordinals until I got to 10th which failed. Still impressive.</p> <p>Line fitting should work better for longer sequences in theory. More data points, better line.</p> <p>Note that there is a 50% chance for the order to flip because the direction is arbitrary. The PCA algorithm could choose either of the two opposing vectors that represent the same line.</p> <p>How about days of the week?</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 162"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> PC1 </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="43.77868402146487" y1="48" x2="43.77868402146487" y2="54" stroke-width="1"></line> , <line x1="69.90006284457209" y1="48" x2="69.90006284457209" y2="54" stroke-width="1"></line> , <line x1="96.02144166767931" y1="48" x2="96.02144166767931" y2="54" stroke-width="1"></line> , <line x1="122.14282049078653" y1="48" x2="122.14282049078653" y2="54" stroke-width="1"></line> , <line x1="148.26419931389376" y1="48" x2="148.26419931389376" y2="54" stroke-width="1"></line> , <line x1="174.38557813700098" y1="48" x2="174.38557813700098" y2="54" stroke-width="1"></line> , <line x1="200.5069569601082" y1="48" x2="200.5069569601082" y2="54" stroke-width="1"></line> , <line x1="226.62833578321542" y1="48" x2="226.62833578321542" y2="54" stroke-width="1"></line> , <line x1="252.74971460632264" y1="48" x2="252.74971460632264" y2="54" stroke-width="1"></line> , <line x1="278.87109342942983" y1="48" x2="278.87109342942983" y2="54" stroke-width="1"></line> , <line x1="304.9924722525371" y1="48" x2="304.9924722525371" y2="54" stroke-width="1"></line> , <line x1="331.1138510756443" y1="48" x2="331.1138510756443" y2="54" stroke-width="1"></line> , <line x1="357.2352298987515" y1="48" x2="357.2352298987515" y2="54" stroke-width="1"></line> , <line x1="383.3566087218588" y1="48" x2="383.3566087218588" y2="54" stroke-width="1"></line> <line x1="200.50695696010823" y1="48" x2="200.50695696010823" y2="54" stroke-width="1.5"></line> <text x="200.50695696010823" y="45" text-anchor="middle">0</text> <g> <circle cx="55.666666666666686" cy="51" r="7" fill="transparent"></circle> <circle cx="55.666666666666686" cy="51" r="3"></circle> <text x="55.666666666666686" y="44" text-anchor="middle">-0.28</text> <text x="52.666666666666686" y="84" text-anchor="end">saturday</text> <line x1="55.666666666666686" y1="54" x2="55.666666666666686" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="55.76846138701164" cy="51" r="7" fill="transparent"></circle> <circle cx="55.76846138701164" cy="51" r="3"></circle> <text x="55.76846138701164" y="44" text-anchor="middle">-0.28</text> <text x="52.76846138701164" y="96" text-anchor="end">sunday</text> <line x1="55.76846138701164" y1="54" x2="55.76846138701164" y2="96" stroke-width="1"></line> </g>,<g> <circle cx="295.5932625632968" cy="51" r="7" fill="transparent"></circle> <circle cx="295.5932625632968" cy="51" r="3"></circle> <text x="295.5932625632968" y="44" text-anchor="middle">0.18</text> <text x="292.5932625632968" y="108" text-anchor="end">friday</text> <line x1="295.5932625632968" y1="54" x2="295.5932625632968" y2="108" stroke-width="1"></line> </g>,<g> <circle cx="360.483564460615" cy="51" r="7" fill="transparent"></circle> <circle cx="360.483564460615" cy="51" r="3"></circle> <text x="360.483564460615" y="44" text-anchor="middle">0.31</text> <text x="357.483564460615" y="120" text-anchor="end">monday</text> <line x1="360.483564460615" y1="54" x2="360.483564460615" y2="120" stroke-width="1"></line> </g>,<g> <circle cx="363.8524824161866" cy="51" r="7" fill="transparent"></circle> <circle cx="363.8524824161866" cy="51" r="3"></circle> <text x="363.8524824161866" y="44" text-anchor="middle">0.31</text> <text x="360.8524824161866" y="132" text-anchor="end">thursday</text> <line x1="363.8524824161866" y1="54" x2="363.8524824161866" y2="132" stroke-width="1"></line> </g>,<g> <circle cx="370.89010657545566" cy="51" r="7" fill="transparent"></circle> <circle cx="370.89010657545566" cy="51" r="3"></circle> <text x="370.89010657545566" y="44" text-anchor="middle">0.33</text> <text x="367.89010657545566" y="144" text-anchor="end">wednesday</text> <line x1="370.89010657545566" y1="54" x2="370.89010657545566" y2="144" stroke-width="1"></line> </g>,<g> <circle cx="372.33333333333337" cy="51" r="7" fill="transparent"></circle> <circle cx="372.33333333333337" cy="51" r="3"></circle> <text x="372.33333333333337" y="44" text-anchor="middle">0.33</text> <text x="369.33333333333337" y="156" text-anchor="end">tuesday</text> <line x1="372.33333333333337" y1="54" x2="372.33333333333337" y2="156" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>saturday,<wbr>sunday,<wbr>friday,<wbr>monday,<wbr>thursday,<wbr>wednesday,<wbr>tuesday</code> </td></tr></tbody></table> </figure> <p>Days of the week still impossible to sort, or is it?</p> <h2>Rings in conceptual space</h2> <p>Another variant was to use polar coordinates to sort. The idea was that days of the week are cyclical and would form a ring in conceptual space.</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 400 400"><defs> <marker id="scatter-arrow-end" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> <marker id="scatter-arrow-start" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> </defs> <line x1="33.6" y1="200" x2="366.4" y2="200" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="200" y1="309.4" x2="200" y2="90.6" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="76.1" y1="197" x2="76.1" y2="203" stroke-width="1"></line><line x1="101.2" y1="197" x2="101.2" y2="203" stroke-width="1"></line><line x1="126.3" y1="197" x2="126.3" y2="203" stroke-width="1"></line><line x1="151.3" y1="197" x2="151.3" y2="203" stroke-width="1"></line><line x1="176.4" y1="197" x2="176.4" y2="203" stroke-width="1"></line><line x1="201.5" y1="197" x2="201.5" y2="203" stroke-width="1"></line><line x1="226.6" y1="197" x2="226.6" y2="203" stroke-width="1"></line><line x1="251.6" y1="197" x2="251.6" y2="203" stroke-width="1"></line><line x1="276.7" y1="197" x2="276.7" y2="203" stroke-width="1"></line><line x1="301.8" y1="197" x2="301.8" y2="203" stroke-width="1"></line><line x1="326.9" y1="197" x2="326.9" y2="203" stroke-width="1"></line><line x1="197" y1="267.9" x2="203" y2="267.9" stroke-width="1"></line><line x1="197" y1="242.8" x2="203" y2="242.8" stroke-width="1"></line><line x1="197" y1="217.7" x2="203" y2="217.7" stroke-width="1"></line><line x1="197" y1="192.6" x2="203" y2="192.6" stroke-width="1"></line><line x1="197" y1="167.5" x2="203" y2="167.5" stroke-width="1"></line><line x1="197" y1="142.5" x2="203" y2="142.5" stroke-width="1"></line><line x1="197" y1="117.4" x2="203" y2="117.4" stroke-width="1"></line><g> <circle cx="48.1" cy="295.0" r="7" fill="transparent"></circle> <circle cx="48.1" cy="295.0" r="3"></circle> <text x="55.1" y="299.0" text-anchor="start">sunday</text> <text x="55.1" y="309.0" text-anchor="start">(-0.41, -0.20)</text> </g>,<g> <circle cx="340.6" cy="218.7" r="7" fill="transparent"></circle> <circle cx="340.6" cy="218.7" r="3"></circle> <text x="333.6" y="222.7" text-anchor="end">monday</text> <text x="333.6" y="232.7" text-anchor="end">(0.18, -0.05)</text> </g>,<g> <circle cx="350.6" cy="207.4" r="7" fill="transparent"></circle> <circle cx="350.6" cy="207.4" r="3"></circle> <text x="343.6" y="211.4" text-anchor="end">wednesday</text> <text x="343.6" y="221.4" text-anchor="end">(0.20, -0.03)</text> </g>,<g> <circle cx="352.0" cy="204.8" r="7" fill="transparent"></circle> <circle cx="352.0" cy="204.8" r="3"></circle> <text x="345.0" y="208.8" text-anchor="end">tuesday</text> <text x="345.0" y="218.8" text-anchor="end">(0.20, -0.02)</text> </g>,<g> <circle cx="343.9" cy="189.5" r="7" fill="transparent"></circle> <circle cx="343.9" cy="189.5" r="3"></circle> <text x="336.9" y="193.5" text-anchor="end">thursday</text> <text x="336.9" y="203.5" text-anchor="end">(0.18, 0.01)</text> </g>,<g> <circle cx="278.3" cy="127.9" r="7" fill="transparent"></circle> <circle cx="278.3" cy="127.9" r="3"></circle> <text x="271.3" y="131.9" text-anchor="end">friday</text> <text x="271.3" y="141.9" text-anchor="end">(0.05, 0.13)</text> </g>,<g> <circle cx="48.0" cy="105.0" r="7" fill="transparent"></circle> <circle cx="48.0" cy="105.0" r="3"></circle> <text x="55.0" y="109.0" text-anchor="start">saturday</text> <text x="55.0" y="119.0" text-anchor="start">(-0.41, 0.17)</text> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>sunday,<wbr>monday,<wbr>wednesday,<wbr>tuesday,<wbr>thursday,<wbr>friday,<wbr>saturday</code> </td></tr></tbody></table> </figure> <p>Almost there! I had a hunch that the weekends gain slightly different meanings from the rest of the week and so became outliers. If we removed them?</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 400 400"><defs> <marker id="scatter-arrow-end" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> <marker id="scatter-arrow-start" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> </defs> <line x1="33.6" y1="200" x2="366.4" y2="200" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="200" y1="306.7" x2="200" y2="93.3" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="99.7" y1="197" x2="99.7" y2="203" stroke-width="1"></line><line x1="141.2" y1="197" x2="141.2" y2="203" stroke-width="1"></line><line x1="182.7" y1="197" x2="182.7" y2="203" stroke-width="1"></line><line x1="224.1" y1="197" x2="224.1" y2="203" stroke-width="1"></line><line x1="265.6" y1="197" x2="265.6" y2="203" stroke-width="1"></line><line x1="307.1" y1="197" x2="307.1" y2="203" stroke-width="1"></line><line x1="197" y1="280.9" x2="203" y2="280.9" stroke-width="1"></line><line x1="197" y1="264.3" x2="203" y2="264.3" stroke-width="1"></line><line x1="197" y1="247.7" x2="203" y2="247.7" stroke-width="1"></line><line x1="197" y1="231.1" x2="203" y2="231.1" stroke-width="1"></line><line x1="197" y1="214.5" x2="203" y2="214.5" stroke-width="1"></line><line x1="197" y1="197.9" x2="203" y2="197.9" stroke-width="1"></line><line x1="197" y1="181.3" x2="203" y2="181.3" stroke-width="1"></line><line x1="197" y1="164.8" x2="203" y2="164.8" stroke-width="1"></line><line x1="197" y1="148.2" x2="203" y2="148.2" stroke-width="1"></line><line x1="197" y1="131.6" x2="203" y2="131.6" stroke-width="1"></line><g> <circle cx="352.0" cy="239.4" r="7" fill="transparent"></circle> <circle cx="352.0" cy="239.4" r="3"></circle> <text x="345.0" y="243.4" text-anchor="end">friday</text> <text x="345.0" y="253.4" text-anchor="end">(0.25, -0.01)</text> </g>,<g> <circle cx="114.7" cy="107.7" r="7" fill="transparent"></circle> <circle cx="114.7" cy="107.7" r="3"></circle> <text x="121.7" y="111.7" text-anchor="start">monday</text> <text x="121.7" y="121.7" text-anchor="start">(-0.03, 0.15)</text> </g>,<g> <circle cx="48.0" cy="232.0" r="7" fill="transparent"></circle> <circle cx="48.0" cy="232.0" r="3"></circle> <text x="55.0" y="236.0" text-anchor="start">tuesday</text> <text x="55.0" y="246.0" text-anchor="start">(-0.11, -0.00)</text> </g>,<g> <circle cx="62.5" cy="284.2" r="7" fill="transparent"></circle> <circle cx="62.5" cy="284.2" r="3"></circle> <text x="69.5" y="288.2" text-anchor="start">wednesday</text> <text x="69.5" y="298.2" text-anchor="start">(-0.09, -0.06)</text> </g>,<g> <circle cx="128.7" cy="292.3" r="7" fill="transparent"></circle> <circle cx="128.7" cy="292.3" r="3"></circle> <text x="135.7" y="296.3" text-anchor="start">thursday</text> <text x="135.7" y="306.3" text-anchor="start">(-0.02, -0.07)</text> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>friday,<wbr>monday,<wbr>tuesday,<wbr>wednesday,<wbr>thursday</code> </td></tr></tbody></table> </figure> <p>It’s a cycle, it’s a ring!? at least in 2D PCA projection! What about months?</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 400 400"><defs> <marker id="scatter-arrow-end" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> <marker id="scatter-arrow-start" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> </defs> <line x1="33.6" y1="200" x2="366.4" y2="200" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="200" y1="267.1" x2="200" y2="132.9" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="79.1" y1="197" x2="79.1" y2="203" stroke-width="1"></line><line x1="101.3" y1="197" x2="101.3" y2="203" stroke-width="1"></line><line x1="123.4" y1="197" x2="123.4" y2="203" stroke-width="1"></line><line x1="145.5" y1="197" x2="145.5" y2="203" stroke-width="1"></line><line x1="167.6" y1="197" x2="167.6" y2="203" stroke-width="1"></line><line x1="189.7" y1="197" x2="189.7" y2="203" stroke-width="1"></line><line x1="211.9" y1="197" x2="211.9" y2="203" stroke-width="1"></line><line x1="234.0" y1="197" x2="234.0" y2="203" stroke-width="1"></line><line x1="256.1" y1="197" x2="256.1" y2="203" stroke-width="1"></line><line x1="278.2" y1="197" x2="278.2" y2="203" stroke-width="1"></line><line x1="300.4" y1="197" x2="300.4" y2="203" stroke-width="1"></line><line x1="322.5" y1="197" x2="322.5" y2="203" stroke-width="1"></line><line x1="197" y1="239.6" x2="203" y2="239.6" stroke-width="1"></line><line x1="197" y1="228.6" x2="203" y2="228.6" stroke-width="1"></line><line x1="197" y1="217.5" x2="203" y2="217.5" stroke-width="1"></line><line x1="197" y1="206.4" x2="203" y2="206.4" stroke-width="1"></line><line x1="197" y1="195.4" x2="203" y2="195.4" stroke-width="1"></line><line x1="197" y1="184.3" x2="203" y2="184.3" stroke-width="1"></line><line x1="197" y1="173.3" x2="203" y2="173.3" stroke-width="1"></line><line x1="197" y1="162.2" x2="203" y2="162.2" stroke-width="1"></line><g> <circle cx="48.0" cy="195.2" r="7" fill="transparent"></circle> <circle cx="48.0" cy="195.2" r="3"></circle> <text x="55.0" y="199.2" text-anchor="start">may</text> <text x="55.0" y="209.2" text-anchor="start">(-1.24, 0.00)</text> </g>,<g> <circle cx="344.7" cy="252.7" r="7" fill="transparent"></circle> <circle cx="344.7" cy="252.7" r="3"></circle> <text x="337.7" y="256.7" text-anchor="end">july</text> <text x="337.7" y="266.7" text-anchor="end">(0.10, -0.26)</text> </g>,<g> <circle cx="344.5" cy="246.4" r="7" fill="transparent"></circle> <circle cx="344.5" cy="246.4" r="3"></circle> <text x="337.5" y="250.4" text-anchor="end">april</text> <text x="337.5" y="260.4" text-anchor="end">(0.10, -0.23)</text> </g>,<g> <circle cx="347.0" cy="246.7" r="7" fill="transparent"></circle> <circle cx="347.0" cy="246.7" r="3"></circle> <text x="340.0" y="250.7" text-anchor="end">june</text> <text x="340.0" y="260.7" text-anchor="end">(0.11, -0.23)</text> </g>,<g> <circle cx="349.8" cy="225.0" r="7" fill="transparent"></circle> <circle cx="349.8" cy="225.0" r="3"></circle> <text x="342.8" y="229.0" text-anchor="end">march</text> <text x="342.8" y="239.0" text-anchor="end">(0.12, -0.13)</text> </g>,<g> <circle cx="351.5" cy="197.0" r="7" fill="transparent"></circle> <circle cx="351.5" cy="197.0" r="3"></circle> <text x="344.5" y="201.0" text-anchor="end">august</text> <text x="344.5" y="211.0" text-anchor="end">(0.13, -0.01)</text> </g>,<g> <circle cx="349.4" cy="185.3" r="7" fill="transparent"></circle> <circle cx="349.4" cy="185.3" r="3"></circle> <text x="342.4" y="189.3" text-anchor="end">september</text> <text x="342.4" y="199.3" text-anchor="end">(0.12, 0.05)</text> </g>,<g> <circle cx="352.0" cy="174.9" r="7" fill="transparent"></circle> <circle cx="352.0" cy="174.9" r="3"></circle> <text x="345.0" y="178.9" text-anchor="end">october</text> <text x="345.0" y="188.9" text-anchor="end">(0.13, 0.09)</text> </g>,<g> <circle cx="350.0" cy="166.4" r="7" fill="transparent"></circle> <circle cx="350.0" cy="166.4" r="3"></circle> <text x="343.0" y="170.4" text-anchor="end">february</text> <text x="343.0" y="180.4" text-anchor="end">(0.12, 0.13)</text> </g>,<g> <circle cx="345.4" cy="152.4" r="7" fill="transparent"></circle> <circle cx="345.4" cy="152.4" r="3"></circle> <text x="338.4" y="156.4" text-anchor="end">january</text> <text x="338.4" y="166.4" text-anchor="end">(0.10, 0.19)</text> </g>,<g> <circle cx="347.5" cy="147.3" r="7" fill="transparent"></circle> <circle cx="347.5" cy="147.3" r="3"></circle> <text x="340.5" y="151.3" text-anchor="end">december</text> <text x="340.5" y="161.3" text-anchor="end">(0.11, 0.22)</text> </g>,<g> <circle cx="340.0" cy="155.2" r="7" fill="transparent"></circle> <circle cx="340.0" cy="155.2" r="3"></circle> <text x="333.0" y="159.2" text-anchor="end">november</text> <text x="333.0" y="169.2" text-anchor="end">(0.08, 0.18)</text> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>may,<wbr>july,<wbr>april,<wbr>june,<wbr>march,<wbr>august,<wbr>september,<wbr>october,<wbr>february,<wbr>january,<wbr>december,<wbr>november</code> </td></tr></tbody></table> </figure> <p>Removing the <code>"may"</code> outlier:</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 400 400"><defs> <marker id="scatter-arrow-end" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> <marker id="scatter-arrow-start" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> </defs> <line x1="33.6" y1="200" x2="366.4" y2="200" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="200" y1="333.8" x2="200" y2="66.2" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="85.7" y1="197" x2="85.7" y2="203" stroke-width="1"></line><line x1="117.6" y1="197" x2="117.6" y2="203" stroke-width="1"></line><line x1="149.5" y1="197" x2="149.5" y2="203" stroke-width="1"></line><line x1="181.4" y1="197" x2="181.4" y2="203" stroke-width="1"></line><line x1="213.3" y1="197" x2="213.3" y2="203" stroke-width="1"></line><line x1="245.2" y1="197" x2="245.2" y2="203" stroke-width="1"></line><line x1="277.1" y1="197" x2="277.1" y2="203" stroke-width="1"></line><line x1="309.0" y1="197" x2="309.0" y2="203" stroke-width="1"></line><line x1="197" y1="296.4" x2="203" y2="296.4" stroke-width="1"></line><line x1="197" y1="264.5" x2="203" y2="264.5" stroke-width="1"></line><line x1="197" y1="232.6" x2="203" y2="232.6" stroke-width="1"></line><line x1="197" y1="200.7" x2="203" y2="200.7" stroke-width="1"></line><line x1="197" y1="168.8" x2="203" y2="168.8" stroke-width="1"></line><line x1="197" y1="136.9" x2="203" y2="136.9" stroke-width="1"></line><line x1="197" y1="105.0" x2="203" y2="105.0" stroke-width="1"></line><g> <circle cx="48.0" cy="132.6" r="7" fill="transparent"></circle> <circle cx="48.0" cy="132.6" r="3"></circle> <text x="55.0" y="136.6" text-anchor="start">july</text> <text x="55.0" y="146.6" text-anchor="start">(-0.26, 0.11)</text> </g>,<g> <circle cx="65.2" cy="193.3" r="7" fill="transparent"></circle> <circle cx="65.2" cy="193.3" r="3"></circle> <text x="72.2" y="197.3" text-anchor="start">june</text> <text x="72.2" y="207.3" text-anchor="start">(-0.23, 0.01)</text> </g>,<g> <circle cx="66.1" cy="280.5" r="7" fill="transparent"></circle> <circle cx="66.1" cy="280.5" r="3"></circle> <text x="73.1" y="284.5" text-anchor="start">april</text> <text x="73.1" y="294.5" text-anchor="start">(-0.23, -0.13)</text> </g>,<g> <circle cx="128.0" cy="319.4" r="7" fill="transparent"></circle> <circle cx="128.0" cy="319.4" r="3"></circle> <text x="135.0" y="323.4" text-anchor="start">march</text> <text x="135.0" y="333.4" text-anchor="start">(-0.13, -0.19)</text> </g>,<g> <circle cx="296.9" cy="251.8" r="7" fill="transparent"></circle> <circle cx="296.9" cy="251.8" r="3"></circle> <text x="289.9" y="255.8" text-anchor="end">february</text> <text x="289.9" y="265.8" text-anchor="end">(0.13, -0.08)</text> </g>,<g> <circle cx="329.0" cy="256.4" r="7" fill="transparent"></circle> <circle cx="329.0" cy="256.4" r="3"></circle> <text x="322.0" y="260.4" text-anchor="end">november</text> <text x="322.0" y="270.4" text-anchor="end">(0.18, -0.09)</text> </g>,<g> <circle cx="337.3" cy="222.3" r="7" fill="transparent"></circle> <circle cx="337.3" cy="222.3" r="3"></circle> <text x="330.3" y="226.3" text-anchor="end">january</text> <text x="330.3" y="236.3" text-anchor="end">(0.19, -0.03)</text> </g>,<g> <circle cx="352.0" cy="206.3" r="7" fill="transparent"></circle> <circle cx="352.0" cy="206.3" r="3"></circle> <text x="345.0" y="210.3" text-anchor="end">december</text> <text x="345.0" y="220.3" text-anchor="end">(0.22, -0.01)</text> </g>,<g> <circle cx="272.6" cy="172.8" r="7" fill="transparent"></circle> <circle cx="272.6" cy="172.8" r="3"></circle> <text x="265.6" y="176.8" text-anchor="end">october</text> <text x="265.6" y="186.8" text-anchor="end">(0.09, 0.04)</text> </g>,<g> <circle cx="242.5" cy="80.6" r="7" fill="transparent"></circle> <circle cx="242.5" cy="80.6" r="3"></circle> <text x="235.5" y="84.6" text-anchor="end">september</text> <text x="235.5" y="94.6" text-anchor="end">(0.05, 0.19)</text> </g>,<g> <circle cx="208.7" cy="91.7" r="7" fill="transparent"></circle> <circle cx="208.7" cy="91.7" r="3"></circle> <text x="215.7" y="95.7" text-anchor="start">august</text> <text x="215.7" y="105.7" text-anchor="start">(-0.01, 0.17)</text> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>july,<wbr>june,<wbr>april,<wbr>march,<wbr>february,<wbr>november,<wbr>january,<wbr>december,<wbr>october,<wbr>september,<wbr>august</code> </td></tr></tbody></table> </figure> <p>Without May, it’s actually a ring! The starting element is arbitrary when sorting by angle but the order is almost correct! if not for November.</p> <figure> <video autoplay="" playsinline="" loop="" muted="" src="https://leanrada.com/notes/naturalest-sort/months-projector.mp4?ref=rss"></video> <figcaption>In 3D PCA it’s a ring and <em>a dot in front</em>. You can see the how the 1D, 2D, and 2D-without-may projections relate this 3D projection.</figcaption> </figure> <p>There is definitely some value in contextualising the meaning of each word in the list before sorting. I haven’t looked into that direction yet.</p> <p>Should seasons form a cycle?</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 400 400"><defs> <marker id="scatter-arrow-end" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> <marker id="scatter-arrow-start" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> </defs> <line x1="33.6" y1="200" x2="366.4" y2="200" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="200" y1="359.7" x2="200" y2="40.3" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="101.4" y1="197" x2="101.4" y2="203" stroke-width="1"></line><line x1="140.7" y1="197" x2="140.7" y2="203" stroke-width="1"></line><line x1="180.0" y1="197" x2="180.0" y2="203" stroke-width="1"></line><line x1="219.3" y1="197" x2="219.3" y2="203" stroke-width="1"></line><line x1="258.6" y1="197" x2="258.6" y2="203" stroke-width="1"></line><line x1="297.9" y1="197" x2="297.9" y2="203" stroke-width="1"></line><line x1="197" y1="293.8" x2="203" y2="293.8" stroke-width="1"></line><line x1="197" y1="254.5" x2="203" y2="254.5" stroke-width="1"></line><line x1="197" y1="215.2" x2="203" y2="215.2" stroke-width="1"></line><line x1="197" y1="175.9" x2="203" y2="175.9" stroke-width="1"></line><line x1="197" y1="136.7" x2="203" y2="136.7" stroke-width="1"></line><line x1="197" y1="97.4" x2="203" y2="97.4" stroke-width="1"></line><g> <circle cx="48.0" cy="201.2" r="7" fill="transparent"></circle> <circle cx="48.0" cy="201.2" r="3"></circle> <text x="55.0" y="205.2" text-anchor="start">autumn</text> <text x="55.0" y="215.2" text-anchor="start">(-0.54, -0.06)</text> </g>,<g> <circle cx="352.0" cy="345.3" r="7" fill="transparent"></circle> <circle cx="352.0" cy="345.3" r="3"></circle> <text x="345.0" y="349.3" text-anchor="end">winter</text> <text x="345.0" y="359.3" text-anchor="end">(0.24, -0.43)</text> </g>,<g> <circle cx="335.8" cy="102.6" r="7" fill="transparent"></circle> <circle cx="335.8" cy="102.6" r="3"></circle> <text x="328.8" y="106.6" text-anchor="end">summer</text> <text x="328.8" y="116.6" text-anchor="end">(0.20, 0.19)</text> </g>,<g> <circle cx="298.5" cy="54.7" r="7" fill="transparent"></circle> <circle cx="298.5" cy="54.7" r="3"></circle> <text x="291.5" y="58.7" text-anchor="end">spring</text> <text x="291.5" y="68.7" text-anchor="end">(0.10, 0.31)</text> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>autumn,<wbr>winter,<wbr>summer,<wbr>spring</code> </td></tr></tbody></table> </figure> <p>Do random words form a ring?</p> <figure> <svg viewBox="0 0 400 400"><defs> <marker id="scatter-arrow-end" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> <marker id="scatter-arrow-start" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> </defs> <line x1="33.6" y1="200" x2="366.4" y2="200" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="200" y1="344.9" x2="200" y2="55.1" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="66.2" y1="197" x2="66.2" y2="203" stroke-width="1"></line><line x1="89.9" y1="197" x2="89.9" y2="203" stroke-width="1"></line><line x1="113.5" y1="197" x2="113.5" y2="203" stroke-width="1"></line><line x1="137.2" y1="197" x2="137.2" y2="203" stroke-width="1"></line><line x1="160.8" y1="197" x2="160.8" y2="203" stroke-width="1"></line><line x1="184.5" y1="197" x2="184.5" y2="203" stroke-width="1"></line><line x1="208.1" y1="197" x2="208.1" y2="203" stroke-width="1"></line><line x1="231.8" y1="197" x2="231.8" y2="203" stroke-width="1"></line><line x1="255.4" y1="197" x2="255.4" y2="203" stroke-width="1"></line><line x1="279.1" y1="197" x2="279.1" y2="203" stroke-width="1"></line><line x1="302.7" y1="197" x2="302.7" y2="203" stroke-width="1"></line><line x1="326.4" y1="197" x2="326.4" y2="203" stroke-width="1"></line><line x1="197" y1="299.4" x2="203" y2="299.4" stroke-width="1"></line><line x1="197" y1="275.8" x2="203" y2="275.8" stroke-width="1"></line><line x1="197" y1="252.1" x2="203" y2="252.1" stroke-width="1"></line><line x1="197" y1="228.5" x2="203" y2="228.5" stroke-width="1"></line><line x1="197" y1="204.8" x2="203" y2="204.8" stroke-width="1"></line><line x1="197" y1="181.2" x2="203" y2="181.2" stroke-width="1"></line><line x1="197" y1="157.5" x2="203" y2="157.5" stroke-width="1"></line><line x1="197" y1="133.9" x2="203" y2="133.9" stroke-width="1"></line><line x1="197" y1="110.2" x2="203" y2="110.2" stroke-width="1"></line><line x1="197" y1="86.6" x2="203" y2="86.6" stroke-width="1"></line><g> <circle cx="243.7" cy="275.4" r="7" fill="transparent"></circle> <circle cx="243.7" cy="275.4" r="3"></circle> <text x="236.7" y="279.4" text-anchor="end">practical</text> <text x="236.7" y="289.4" text-anchor="end">(0.05, -0.40)</text> </g>,<g> <circle cx="189.9" cy="330.5" r="7" fill="transparent"></circle> <circle cx="189.9" cy="330.5" r="3"></circle> <text x="196.9" y="334.5" text-anchor="start">detail</text> <text x="196.9" y="344.5" text-anchor="start">(-0.18, -0.63)</text> </g>,<g> <circle cx="352.0" cy="71.8" r="7" fill="transparent"></circle> <circle cx="352.0" cy="71.8" r="3"></circle> <text x="345.0" y="75.8" text-anchor="end">resist</text> <text x="345.0" y="85.8" text-anchor="end">(0.51, 0.46)</text> </g>,<g> <circle cx="325.4" cy="158.6" r="7" fill="transparent"></circle> <circle cx="325.4" cy="158.6" r="3"></circle> <text x="318.4" y="162.6" text-anchor="end">do</text> <text x="318.4" y="172.6" text-anchor="end">(0.40, 0.10)</text> </g>,<g> <circle cx="48.0" cy="69.5" r="7" fill="transparent"></circle> <circle cx="48.0" cy="69.5" r="3"></circle> <text x="55.0" y="73.5" text-anchor="start">tower</text> <text x="55.0" y="83.5" text-anchor="start">(-0.78, 0.47)</text> </g></svg> </figure> <figure> <svg viewBox="0 0 400 400"><defs> <marker id="scatter-arrow-end" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> <marker id="scatter-arrow-start" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> </defs> <line x1="37.6" y1="200" x2="362.4" y2="200" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="200" y1="366.4" x2="200" y2="33.6" stroke-width="1" marker-start="url(#scatter-arrow-start)" marker-end="url(#scatter-arrow-end)"></line> <line x1="87.8" y1="197" x2="87.8" y2="203" stroke-width="1"></line><line x1="112.8" y1="197" x2="112.8" y2="203" stroke-width="1"></line><line x1="137.9" y1="197" x2="137.9" y2="203" stroke-width="1"></line><line x1="162.9" y1="197" x2="162.9" y2="203" stroke-width="1"></line><line x1="188.0" y1="197" x2="188.0" y2="203" stroke-width="1"></line><line x1="213.0" y1="197" x2="213.0" y2="203" stroke-width="1"></line><line x1="238.1" y1="197" x2="238.1" y2="203" stroke-width="1"></line><line x1="263.2" y1="197" x2="263.2" y2="203" stroke-width="1"></line><line x1="288.2" y1="197" x2="288.2" y2="203" stroke-width="1"></line><line x1="313.3" y1="197" x2="313.3" y2="203" stroke-width="1"></line><line x1="197" y1="325.5" x2="203" y2="325.5" stroke-width="1"></line><line x1="197" y1="300.5" x2="203" y2="300.5" stroke-width="1"></line><line x1="197" y1="275.4" x2="203" y2="275.4" stroke-width="1"></line><line x1="197" y1="250.3" x2="203" y2="250.3" stroke-width="1"></line><line x1="197" y1="225.3" x2="203" y2="225.3" stroke-width="1"></line><line x1="197" y1="200.2" x2="203" y2="200.2" stroke-width="1"></line><line x1="197" y1="175.2" x2="203" y2="175.2" stroke-width="1"></line><line x1="197" y1="150.1" x2="203" y2="150.1" stroke-width="1"></line><line x1="197" y1="125.1" x2="203" y2="125.1" stroke-width="1"></line><line x1="197" y1="100.0" x2="203" y2="100.0" stroke-width="1"></line><line x1="197" y1="75.0" x2="203" y2="75.0" stroke-width="1"></line><g> <circle cx="52.0" cy="314.6" r="7" fill="transparent"></circle> <circle cx="52.0" cy="314.6" r="3"></circle> <text x="59.0" y="318.6" text-anchor="start">panther</text> <text x="59.0" y="328.6" text-anchor="start">(-0.54, -0.26)</text> </g>,<g> <circle cx="119.4" cy="48.0" r="7" fill="transparent"></circle> <circle cx="119.4" cy="48.0" r="3"></circle> <text x="126.4" y="52.0" text-anchor="start">finch</text> <text x="126.4" y="62.0" text-anchor="start">(-0.27, 0.81)</text> </g>,<g> <circle cx="348.0" cy="222.9" r="7" fill="transparent"></circle> <circle cx="348.0" cy="222.9" r="3"></circle> <text x="341.0" y="226.9" text-anchor="end">chicken</text> <text x="341.0" y="236.9" text-anchor="end">(0.64, 0.11)</text> </g>,<g> <circle cx="328.1" cy="314.2" r="7" fill="transparent"></circle> <circle cx="328.1" cy="314.2" r="3"></circle> <text x="321.1" y="318.2" text-anchor="end">camel</text> <text x="321.1" y="328.2" text-anchor="end">(0.56, -0.25)</text> </g>,<g> <circle cx="92.5" cy="352.0" r="7" fill="transparent"></circle> <circle cx="92.5" cy="352.0" r="3"></circle> <text x="99.5" y="356.0" text-anchor="start">jaguar</text> <text x="99.5" y="366.0" text-anchor="start">(-0.38, -0.41)</text> </g></svg> </figure> <p>I don’t know what to make of these. In the end I didn’t include angular sorting because there was no good crieria for when to use it and when to use linear projections.</p> <h2>Natural sorting showdown</h2> <p>If we were to put orderings on a scale, we can put dictionary sorting on one extreme and LLM on the other end (or maybe an actual human). In the middle lies our solution which I’ll call <code>wordSort</code>, coming after <code>naturalSort</code>. <small>Wait, did I just order the orderings? Sorted the sorts?</small></p> <table> <tbody><tr><th>Expected</th><th><code>Array.sort</code><br>(lexical)</th><th><code>naturalSort</code><br>(natural)</th><th><code>wordSort</code><br>(naturaler)</th><th><code>vibeSort</code><br>(naturalest) </th></tr><tr><td>a, b, c</td><td><details><summary>✅</summary>a,<br>b,<br>c</details></td><td><details><summary>✅</summary>a,<br>b,<br>c</details></td><td><details><summary>🔁</summary>c,<br>b,<br>a</details></td><td><details><summary>✅</summary>a,<br>b,<br>c</details> </td></tr><tr><td>low, medium, high</td><td><details open=""><summary>❌</summary>high,<br>low,<br>medium</details></td><td><details open=""><summary>❌</summary>high,<br>low,<br>medium</details></td><td><details open=""><summary>✅</summary>low,<br>medium,<br>high</details></td><td><details open=""><summary>✅</summary>low,<br>medium,<br>high</details> </td></tr><tr><td>birth, life, death</td><td><details><summary>❌</summary>birth,<br>death,<br>life</details></td><td><details><summary>❌</summary>birth,<br>death,<br>life</details></td><td><details><summary>✅</summary>birth,<br>life,<br>death</details></td><td><details><summary>✅</summary>birth,<br>life,<br>death</details> </td></tr><tr><td>dawn, noon, dusk</td><td><details><summary>❌</summary>dawn,<br>dusk,<br>noon</details></td><td><details><summary>❌</summary>dawn,<br>dusk,<br>noon</details></td><td><details><summary>✅</summary>dawn,<br>noon,<br>dusk</details></td><td><details><summary>✅</summary>dawn,<br>noon,<br>dusk</details> </td></tr><tr><td>rare, uncommon, common</td><td><details><summary>❌</summary>common,<br>rare,<br>uncommon</details></td><td><details><summary>❌</summary>common,<br>rare,<br>uncommon</details></td><td><details><summary>✅</summary>rare,<br>uncommon,<br>common</details></td><td><details><summary>🔁</summary>common,<br>uncommon,<br>rare</details> </td></tr><tr><td>bronze, silver, gold</td><td><details><summary>❌</summary>bronze,<br>gold,<br>silver</details></td><td><details><summary>❌</summary>bronze,<br>gold,<br>silver</details></td><td><details><summary>🔁</summary>gold,<br>silver,<br>bronze</details></td><td><details><summary>✅</summary>bronze,<br>silver,<br>gold</details> </td></tr><tr><td>Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday</td><td><details><summary>❌</summary>Friday,<br>Monday,<br>Saturday,<br>Sunday,<br>Thursday,<br>Tuesday,<br>Wednesday</details></td><td><details><summary>❌</summary>Friday,<br>Monday,<br>Saturday,<br>Sunday,<br>Thursday,<br>Tuesday,<br>Wednesday</details></td><td><details><summary>❌</summary>Monday,<br>Wednesday,<br>Thursday,<br>Tuesday,<br>Friday,<br>Saturday,<br>Sunday</details></td><td><details><summary>❌</summary>Sunday,<br>Monday,<br>Tuesday,<br>Wednesday,<br>Thursday,<br>Friday,<br>Saturday</details> </td></tr><tr><td>first, second, third, fourth, fifth</td><td><details><summary>❌</summary>fifth,<br>first,<br>fourth,<br>second,<br>third</details></td><td><details><summary>❌</summary>fifth,<br>first,<br>fourth,<br>second,<br>third</details></td><td><details><summary>❌</summary>first,<br>second,<br>fifth,<br>third,<br>fourth</details></td><td><details><summary>✅</summary>first,<br>second,<br>third,<br>fourth,<br>fifth</details> </td></tr><tr><td>file1, file2, file3, file10, file20</td><td><details><summary>❌</summary>file1,<br>file10,<br>file2,<br>file20,<br>file3</details></td><td><details><summary>✅</summary>file1,<br>file2,<br>file3,<br>file10,<br>file20</details></td><td>N/A</td><td><details><summary>✅</summary>file1,<br>file2,<br>file3,<br>file10,<br>file20</details> </td></tr><tr><td>Wednesday, February 15, 2023; Sunday, December 1, 2024; Wednesday, March 5, 2025</td><td><details><summary>❌</summary>Sunday, December 1, 2024;<br>Wednesday, February 15, 2023;<br>Wednesday, March 5, 2025</details></td><td><details><summary>✅</summary>Wednesday, February 15, 2023;<br>Sunday, December 1, 2024;<br>Wednesday, March 5, 2025</details></td><td>N/A</td><td><details><summary>✅</summary>Wednesday, February 15, 2023;<br>Sunday, December 1, 2024;<br>Wednesday, March 5, 2025</details> </td></tr></tbody></table> <p>Isn’t it ironic that the most natural sorter is artificial intelligence?</p> <h2>Online demo</h2> <p>Congrats on making it this far. As a reward you can play with an online demo! Since this is a web demo, the vocabulary is limited, so bear with it and try to use very common words only!</p> <pre>Interactive content: <a href="https://leanrada.com/notes/naturalest-sort/?ref=rss">Visit the post to interact with this content.</a></pre> <h2>Bonus: Organic sort</h2> <p>If you send a list of words to <code>organic-sort-requests@leanrad.com</code>, I will sort them with my meat brain and send the results back to you.</p> <h2>Conclusion</h2> <p>Experiment was mixed, but the results were surprisingly good. Word embeddings can be used sort words by meaning without any hardcoded rules or LLM calls. It’s not perfect but for 300 dimensions, it’s decent and it can run on a web browser!</p> <p>It seems that word embeddings already encode order implicitly. We just need to find the right axis. For cyclical sequences, polar coordinates on the right plane can get close.</p> <p>This particular implementation lacked a wide variety of order vectors to choose from. This could be mitigated by incorporating more diverse anchor words and increasing the clusters. This could help the algorithm find the perfect axis (or not! It could also add noise).</p> <p>I think the biggest weakness is ambiguity. Word2vec assigns one static vector per word, so words with multiple meanings like <code>"may"</code> get an average vector across all their meanings as in <i>‘if I may’</i> and <i>‘5th of May’</i>. <a href="https://en.wikipedia.org/wiki/Word-sense_disambiguation#Unsupervised_methods" target="_blank">Word-sense disambiguation</a> is its own topic. Maybe a neural network like a transformer could be used to disambiguate words in the context of the full list to sort (<a href="https://arxiv.org/abs/2010.02686" target="_blank">it’s been done</a>), but if you’re gonna run a neural network anyway on every sort then just use a language model and vibe sort it.</p> <p>So this sits in an interesting middle ground between traditional natural sort (which only handles alphanumeric patterns) and LLM-sort (which understands everything but is expensive). Word embeddings are fast, offline, and handle semantic ordering… as long as the words aren’t too ambiguous.</p> <p>Final question: Which came first?</p> <figure> <table> <tbody><tr> <td colspan="2"><svg viewBox="0 0 416 102"><defs> <marker id="arrow-left" markerWidth="4" markerHeight="8" refX="0" refY="4" orient="auto"> <path d="M4,0 L0,4 L4,8" fill="none" stroke-width="1"></path> </marker> <marker id="arrow-right" markerWidth="4" markerHeight="8" refX="4" refY="4" orient="auto"> <path d="M0,0 L4,4 L0,8" fill="none" stroke-width="1"></path> </marker> </defs> <foreignObject x="24" y="12" width="380" height="24"> 'sequence' cluster vector </foreignObject> <line x1="24" y1="51" x2="404" y2="51" stroke-width="1" marker-start="url(#arrow-left)" marker-end="url(#arrow-right)"></line> <line x1="48.636612203833586" y1="48" x2="48.636612203833586" y2="54" stroke-width="1"></line> , <line x1="63.751360051998226" y1="48" x2="63.751360051998226" y2="54" stroke-width="1"></line> , <line x1="78.86610790016286" y1="48" x2="78.86610790016286" y2="54" stroke-width="1"></line> , <line x1="93.9808557483275" y1="48" x2="93.9808557483275" y2="54" stroke-width="1"></line> , <line x1="109.09560359649211" y1="48" x2="109.09560359649211" y2="54" stroke-width="1"></line> , <line x1="124.21035144465672" y1="48" x2="124.21035144465672" y2="54" stroke-width="1"></line> , <line x1="139.32509929282133" y1="48" x2="139.32509929282133" y2="54" stroke-width="1"></line> , <line x1="154.439847140986" y1="48" x2="154.439847140986" y2="54" stroke-width="1"></line> , <line x1="169.5545949891506" y1="48" x2="169.5545949891506" y2="54" stroke-width="1"></line> , <line x1="184.66934283731524" y1="48" x2="184.66934283731524" y2="54" stroke-width="1"></line> , <line x1="199.78409068547984" y1="48" x2="199.78409068547984" y2="54" stroke-width="1"></line> , <line x1="214.8988385336445" y1="48" x2="214.8988385336445" y2="54" stroke-width="1"></line> , <line x1="230.01358638180912" y1="48" x2="230.01358638180912" y2="54" stroke-width="1"></line> , <line x1="245.12833422997375" y1="48" x2="245.12833422997375" y2="54" stroke-width="1"></line> , <line x1="260.24308207813834" y1="48" x2="260.24308207813834" y2="54" stroke-width="1"></line> , <line x1="275.357829926303" y1="48" x2="275.357829926303" y2="54" stroke-width="1"></line> , <line x1="290.47257777446765" y1="48" x2="290.47257777446765" y2="54" stroke-width="1"></line> , <line x1="305.58732562263225" y1="48" x2="305.58732562263225" y2="54" stroke-width="1"></line> , <line x1="320.7020734707969" y1="48" x2="320.7020734707969" y2="54" stroke-width="1"></line> , <line x1="335.8168213189615" y1="48" x2="335.8168213189615" y2="54" stroke-width="1"></line> , <line x1="350.93156916712616" y1="48" x2="350.93156916712616" y2="54" stroke-width="1"></line> , <line x1="366.0463170152908" y1="48" x2="366.0463170152908" y2="54" stroke-width="1"></line> , <line x1="381.1610648634554" y1="48" x2="381.1610648634554" y2="54" stroke-width="1"></line> <line x1="275.357829926303" y1="48" x2="275.357829926303" y2="54" stroke-width="1.5"></line> <text x="275.357829926303" y="45" text-anchor="middle">0</text> <g> <circle cx="55.666666666666664" cy="51" r="7" fill="transparent"></circle> <circle cx="55.666666666666664" cy="51" r="3"></circle> <text x="55.666666666666664" y="44" text-anchor="middle">-0.07</text> <text x="52.666666666666664" y="84" text-anchor="end">egg</text> <line x1="55.666666666666664" y1="54" x2="55.666666666666664" y2="84" stroke-width="1"></line> </g>,<g> <circle cx="372.3333333333333" cy="51" r="7" fill="transparent"></circle> <circle cx="372.3333333333333" cy="51" r="3"></circle> <text x="372.3333333333333" y="44" text-anchor="middle">0.03</text> <text x="369.3333333333333" y="96" text-anchor="end">chicken</text> <line x1="372.3333333333333" y1="54" x2="372.3333333333333" y2="96" stroke-width="1"></line> </g></svg> </td></tr><tr> <td><b>Sorted</b> </td><td><code>egg,<wbr>chicken</code> </td></tr></tbody></table> </figure> <h2>Further reading</h2> <ul> <li><a href="https://blog.codinghorror.com/sorting-for-humans-natural-sort-order/">Sorting for Humans (codinghorror blog, 2007)</a> </li><li><a href="http://www.overset.com/2008/09/01/javascript-natural-sort-algorithm/">naturalSort (overset blog, 2008)</a> </li><li><a href="https://fuzzytolerance.info/blog/2019/07/19/The-better-way-to-do-natural-sort-in-JavaScript/">Native JS via localeCompare (fuzzytolerance blog, 2019)</a> </li><li> <a href="https://arxiv.org/abs/1802.01241">Semantic projection (Grand et al, 2018)</a>, same core idea <img src="https://leanrada.com/notes/naturalest-sort/semantic-projection.png?ref=rss" alt="graph showing an embedding space with a SMALL to BIG line with animals of different sizes projected onto it" width="260" height="193"> </li><li><a href="https://arxiv.org/abs/1806.05521v1">Semantic axis (An, Kwak &amp; Ahn, 2018)</a>, identifying pole words (anchor words) </li><li><a href="https://aclanthology.org/D13-1169/">Deriving Adjectival Scales from Continuous Space Word Representations (Kim &amp; de Marneffe, 2013)</a>, ranking words with static embeddings </li><li><a href="https://arxiv.org/abs/2010.02686">Ranking Scalar Adjectives with Contextualised Representations (Garí Soler &amp; Apidianaki, 2020)</a>, ranking words with contextual embeddings </li></ul>

2026/3/16
阅读更多

Japanese typing in QMK firmware

<p><em>For RSS readers: This article contains interactive content available on the <a href="https://leanrada.com/notes/kana-input-qmk/?ref=rss">original post on leanrada.com</a>.</em></p> <p>Implemented a Japanese input method in my keyboard firmware (based on <a href="https://qmk.fm/" target="_blank">QMK</a>).</p> <p>It’s used to write in <em>katakana</em> and <em>hiragana</em> (syllable-based writing systems for Japanese) using Roman / Latin letters on my keyboard.</p> <video autoplay="" muted="" playsinline="" loop="" src="https://leanrada.com/notes/kana-input-qmk/demo.mp4?ref=rss"></video> <h2>How kana works</h2> <p>Hiragana and katakana (collectively <a href="https://en.wikipedia.org/wiki/Kana" target="_blank"><em>kana</em></a>) are syllabaries, writing systems where a consonant-vowel syllable is written as a single character.</p> <p>A common way to type kana on a regular keyboard is <a href="http://japanese-lesson.com/characters/romaji/index.html" target="_blank"><strong>romaji</strong></a>, using Roman letters to produce kana. For example, <em>sushi</em> = <ruby>す<rt>su</rt>し<rt>shi</rt></ruby>.</p> <p>Kana are usually organised as a table with vowels as columns and consonants as rows:</p> <prose-table> <table> <tbody><tr> <th> </th><th><em>A</em> </th><th><em>I</em> </th><th><em>U</em> </th><th><em>E</em> </th><th><em>O</em> </th></tr><tr> <th><em>K</em> </th><td><ruby>か<rt>ka</rt></ruby> </td><td><ruby>き<rt>ki</rt></ruby> </td><td><ruby>く<rt>ku</rt></ruby> </td><td><ruby>け<rt>ke</rt></ruby> </td><td><ruby>こ<rt>ko</rt></ruby> </td></tr><tr> <th><em>S</em> </th><td><ruby>さ<rt>sa</rt></ruby> </td><td><ruby>し<rt>si/shi</rt></ruby> </td><td><ruby>す<rt>su</rt></ruby> </td><td><ruby>せ<rt>se</rt></ruby> </td><td><ruby>そ<rt>so</rt></ruby> </td></tr><tr> <th><em>T</em> </th><td><ruby>た<rt>ta</rt></ruby> </td><td><ruby>ち<rt>ti/chi</rt></ruby> </td><td><ruby>つ<rt>tu/tsu</rt></ruby> </td><td><ruby>て<rt>te</rt></ruby> </td><td><ruby>と<rt>to</rt></ruby> </td></tr><tr> <td>⋮ </td><td>⋮ </td><td>⋮ </td><td>⋮ </td><td>⋮ </td><td>⋮ </td></tr></tbody></table> </prose-table> <p>This tabular structure inspired the core idea behind my implementation.</p> <h2>The map</h2> <p>The core idea is a 2D lookup table. Since flash memory (program space) is severely limited on keyboard microcontrollers, I wanted to represent all romaji rules in a single compact table.</p> <p>Columns are the five vowels. Rows cover every Roman letter, including those that don’t make sense for Japanese, to keep the indexing logic simple and generalised.</p> <pre><code>static char MAP[ROW_COUNT][5*3] = { // A I U E O "あああああ", // A (vowel) "ばびぶべぼ", // B "?ち???", // C "だ?づでど", // D "えええええ", // E (vowel) "??ふ??", // F "がぎぐげご", // G "はひふへほ", // H "いいいいい", // I (vowel) "?じ???", // J "かきくけこ", // K "らりるれろ", // L "まみむめも", // M "なにぬねの", // N "おおおおお", // O (vowel) "ぱぴぷぺぽ", // P "??く??", // Q "らりるれろ", // R "さしすせそ", // S "た?つてと", // T "ううううう", // U (vowel) "??ゔ??", // V "わ?う?を", // W "ぁぃぅぇぉ", // X (small vowels) "やいゆえよ", // Y "ざじずぜぞ", // Z };</code></pre> <p><small>Some of these code examples are interactive!</small></p> <p>Every letter in the alphabet has a row in the map even if it isn't used for kana to make indexing simple. Since <a href="https://www.kenkoonwong.com/blog/usb-hid-key-press-report/#a-table-of-usage-id" target="_blank">keycodes</a> are assigned alphabetically in a contiguous block, we can use pointer arithmetic <code>MAP[pressed_keycode - keycode('a')]</code> to index the row for any pressed letter key. The offset is just the letter's position in the alphabet.</p> <p>For letter combinations that don't map to anything in Japanese, like <code>Q,A</code>, let’s mark them with <code>?</code> for now. More on special values later.</p> <pre><code>// A I U E O "??く??", // Q</code></pre> <p>The <code>X</code> row is reused to input small vowels ぁぃぅぇぉ (there is no <em>x</em> consonant in Japanese).</p> <pre><code>// A I U E O "ぁぃぅぇぉ", // X (small vowels)</code></pre> <p>Those are the basics of the map lookup. Essentially, a typed consonant and a vowel give us the character for that syllable.</p> <p>But you might be wondering how a 2D table of Unicode characters even works in practice. What about endianness? Doesn't Unicode use variable-length encoding? How would indexing into that work?</p> <h2>UTF-8</h2> <p>The <a href="https://qmk.fm/" target="_blank">QMK</a> firmware framework only supports UTF-8 for Unicode output, so that's the encoding I needed to work with.</p> <p>UTF-8 is a variable-length encoding spanning the full 21-bit Unicode range <code>U+0000</code>–<code>U+10FFFF</code>. The number of bytes each character takes up depends on the character range:</p> <prose-table> <table> <thead> <tr> <th>Unicode character </th><th>Bytes in UTF-8 </th></tr></thead> <tbody> <tr> <td><code>U+0000</code>–<code>U+007F</code> </td><td>1 </td></tr><tr> <td><code>U+0080</code>–<code>U+07FF</code> </td><td>2 </td></tr><tr> <td><code>U+0800</code>–<code>U+FFFF</code> </td><td>3 </td></tr></tbody> </table> </prose-table> <p><a href="https://www.utf8-chartable.de/unicode-utf8-table.pl?start=12352&amp;number=192&amp;view=2" target="_blank">Kana characters fall in the <code>U+3040</code> to <code>U+30FF</code> range</a>, so every kana character is exactly <strong>3 bytes</strong> in UTF-8.</p> <p>This means every row in the map, which is 5 kana characters, is consistently 15 bytes long. The table is evenly laid out in memory! Any character can be addressed with simple index arithmetic:</p> <pre><code>// kana_ptr points to the kana character (3 bytes) char* kana_ptr = &amp;MAP[letter_index][vowel_index * 3];</code></pre> <p>Getting a pointer to <ruby>く<rt>ku</rt></ruby>, for example, is simply <code>&amp;MAP['k' - 'a'][2 * 3]</code>, where 2 is the vowel index of <code>U</code>.</p> <h3>Byte layout of MAP</h3> <pre>Interactive content: <a href="https://leanrada.com/notes/kana-input-qmk/?ref=rss">Visit the post to interact with this content.</a> Alternative name: Figure Alternative text: MAP byte layout illustration</pre> <p>Finally, to send a kana character in QMK from a consonant and vowel keypress:</p> <pre><code>// Get indices uint letter_index = prev_letter_keycode - KC_A; uint vowel_index = vowel_index_for(curr_letter_keycode); // Get pointer to the kana character char* kana_ptr = &amp;MAP[letter_index][vowel_index * 3]; // Copy 3 bytes into a null-terminated buffer static char buf[4] = "\0\0\0\0"; strncpy(buf, kana_ptr, 3); // Send as a null-terminated UTF-8 string send_unicode_string(buf);</code></pre> <h2>Independent vowels</h2> <p>It’s not always a consonant-vowel pair. If the last typed letter wasn’t a consonant, then the current syllable is just a vowel and we can skip the whole syllable processing logic and just output the vowel symbol directly: <code>MAP[letter_index][0]</code>. <small>By the way, we can also use the table to tell whether a letter is a consonant or a vowel. There’s no separate hardcoded list of consonants.</small></p> <pre><code>static char MAP[ROW_COUNT][5*3] = { // A I U E O "あああああ", // ← vowels, just output any of these chars</code></pre> <h2>Special rules</h2> <p>Romaji doesn't always map two letters to one kana. For example, <em>shi</em> in <em>sushi</em> is three letters but a single character (the し in <ruby>す<rt>su</rt>し<rt>shi</rt></ruby>). There are a few of these special rules.</p> <h3>ch and sh</h3> <p>Sometimes two letters represent a single ‘consonant’, like <em>sh</em> and <em>ch</em>. The fix is to resolve the key sequences <code>S,H</code> and <code>C,H</code> to extra ‘virtual consonant’ rows appended at the end of the table. This doesn’t affect the indexing of the real letter rows.</p> <pre><code>static char MAP[ROW_COUNT][5*3] = { // A I U E O // ⋮ "ざじずぜぞ", // Z [ROW_EXTRA_CH] = "?ち???", [ROW_EXTRA_SH] = "?し???", };</code></pre> <p>With this, typing <em>shi</em> outputs し and <em>chi</em> outputs ち as expected.</p> <h3>ja, ju, jo</h3> <p>Some sounds map two Roman letters to two kana characters. For example, <em>ja</em> is <span>じゃ</span> (<ruby>じ<rt>ji</rt></ruby> + small <ruby>や<rt>ya</rt></ruby>). Similarly, <em>ju</em> and <em>jo</em> become <span>じゅ</span> and <span>じょ</span>, respectively. This class of sounds is called <a href="https://www.sljfaq.org/afaq/youon.html" target="_blank"><em>yōon</em></a>, where a syllable ending in <em>i</em> blends into a <em>ya</em>, <em>yu</em>, or <em>yo</em> sound.</p> <p>These get a special marker in the table. Here, it’s a fullwidth Y (also 3 bytes) to signal special handling:</p> <pre><code>static char MAP[ROW_COUNT][5*3] = { // A I U E O // ⋮ "YちY?Y", // C // ⋮ "YじY?Y", // J // ⋮ [ROW_EXTRA_CH] = "YちY?Y", [ROW_EXTRA_SH] = "YしY?Y", [ROW_EXTRA_YOUON] = "ゃ ゅ ょ", };</code></pre> <p>The small ya/yu/yo are stored in an extra row (in columns A, U, and O, respectively).</p> <p>Note how the Y rule integrates in the CH and SH rows seamlessly. Rule synergy!</p> <p>When a lookup returns Y, we know it’s a yōon form. Output the consonant's <em>i</em> form then the vowel’s corresponding small ya/yu/yo:</p> <pre><code>if (match(&amp;MAP[letter_index][vowel_index * 3], "Y")) { send_kana_char(&amp;MAP[letter_index][vowel_offset(KC_I)]); send_kana_char(&amp;MAP[ROW_EXTRA_YOUON][vowel_index * 3]); }</code></pre> <p>That’s not all. We also need to catch the three-letter yōons, such as <em>nya</em>, <em>nyu</em>, and <em>nyo</em>:</p> <pre><code>if ( is_consonant(prev_prev_ keycode) &amp;&amp; prev_consonant == KC_Y &amp;&amp; !empty(&amp;MAP[ROW_EXTRA_YOUON][vowel_offset(curr_vowel)]) ) { send_kana_char( &amp;MAP[prev_prev_keycode - KC_A][vowel_offset(KC_I)]); send_kana_char( &amp;MAP[ROW_EXTRA_YOUON][vowel_offset(curr_vowel)]); }</code></pre> <p>With this, we can type:</p> <prose-table> <table> <tbody><tr> <td>ja </td><td>じゃ </td></tr><tr> <td>nya </td><td>にゃ </td></tr><tr> <td>kyo </td><td>きょ </td></tr><tr> <td>chu </td><td>ちゅ </td></tr><tr> <td>sha </td><td>しゃ </td></tr></tbody></table> </prose-table> <h3>fa, fi, fe, fo</h3> <p>Japanese has no native <em>fa</em>, <em>fi</em>, <em>fe</em>, or <em>fo</em> sounds, only <em>fu</em> (ふ). These foreign sounds are approximated by combining ふ with a small vowel. For example, <em>fa</em>, <em>fi</em>, and <em>fe</em> become <span>ふぁ</span>, <span>ふぃ</span>, and <span>ふぇ</span>, respectively (<span>ファ</span>, <span>フィ</span>, and <span>フェ</span> in katakana).</p> <p>We can mark these cases with the special value <code>S</code> in the map:</p> <pre><code>// A I U E O "SSふSS", // F</code></pre> <p>When a lookup returns <code>S</code>, we need to find the base syllable for that row. Find the column that isn't marked special. For the F row that's the sole <ruby>ふ<rt>fu</rt></ruby>. We then output that base syllable followed by the small vowel from the <code>X</code> row:</p> <pre><code>if (match(&amp;MAP[letter_index][vowel_index * 3], "S")) { uint16_t base_vowel = get_base_vowel(prev_keycode, curr_keycode); send_kana_char( &amp;MAP[prev_keycode - KC_A][vowel_offset(base_vowel)]); send_kana_char( &amp;MAP[ROW_SMALL_VOWELS][vowel_offset(curr_keycode)]); }</code></pre> <p>The same mechanism handles other borrowed sounds like <em>va</em> → ゔぁ and <em>di</em> → でぃ, which are also marked with <code>S</code> in their respective rows.</p> <p>Thus completes the generalised lookup table for romaji.</p> <h2>Final lookup table</h2> <pre><code>// A-Z mapped to kana in UTF-8. // // Since kana is a syllabary, it's a 2D map: // 1. Rows map to one Roman letter in ASCII order. // 2. Columns map to vowels A, I, U, E, O, in that order. // 3. Thus, a syllable is a row-column pair. // // Every char takes up exactly 3 bytes. // Thus it's possible address any syllable by its letter-vowel pair. // - MAP[letter_idx][vowel_idx * 3] // // Special cases: // - Vowels are the same for all columns // - XA, XI, XU, XE, XO are mapped to small vowel kana // - Y indicates youon (never in the I column) // - S indicates additional small vowel must be used static char MAP[ROW_COUNT][5*3] = { // A I U E O "あああああ", // A (vowel) "ばびぶべぼ", // B "YちYSY", // C "だSづでど", // D "えええええ", // E (vowel) "SSふSS", // F "がぎぐげご", // G "はひふへほ", // H "いいいいい", // I (vowel) "YじYSY", // J "かきくけこ", // K "らりるれろ", // L "まみむめも", // M "なにぬねの", // N "おおおおお", // O (vowel) "ぱぴぷぺぽ", // P "SSくSS", // Q "らりるれろ", // R "さしすせそ", // S "たSつてと", // T "ううううう", // U (vowel) "SSゔSS", // V "わSうSを", // W "ぁぃぅぇぉ", // X (small vowels) "やいゆえよ", // Y "ざじずぜぞ", // Z [ROW_EXTRA_YOUON] = "ゃ ゅ ょ", [ROW_EXTRA_CH] = "YちYSY", [ROW_EXTRA_SH] = "YしYSY", }; // enum helps keep track of virtual letter rows and total size enum { ROW_EXTRA_YOUON = 'z' - 'a' + 1, ROW_EXTRA_CH, ROW_EXTRA_SH, ROW_COUNT };</code></pre> <p>Hover over the above highlighted code to see connections!</p> <h2>Actual special cases</h2> <p>Some rules don't fit neatly into the 2D map and are handled separately in logic.</p> <h3>Three-letter sequences</h3> <p>A couple kana can be resolved three specific Roman letters. The only cases are: <em>tsu</em> and <em>dzu</em> for つ and づ. I wasn’t able to find a general pattern that fit the rest so these are detected explicitly before checking the map. If the input matches a known three-letter sequence, we skip the lookup and output the character directly.</p> <h3>Double consonants</h3> <p>In romaji, a doubled consonant indicates a kind of a glottal stop written as small っ. For example, <em>nikki</em> → <ruby>に<rt>ni</rt>っ<rt></rt>き<rt>ki</rt></ruby>. When the same consonant is pressed twice in a row, we output っ and continue processing the second keypress normally.</p> <h3>Standalone n</h3> <p>N (ん) is the only kana consonant that can stand alone without a vowel. But this creates an ambiguity: typing <em>na</em> should produce <ruby>な<rt>na</rt></ruby>, not <ruby>ん<rt>n</rt></ruby> + <ruby>あ<rt>a</rt></ruby>. The solution is to buffer the <em>n</em> and wait for the next keypress. If a vowel or <em>y</em> follows, we proceed with the normal map lookup. If another consonant follows, or the user types <em>n'</em> explicitly, we output ん and move on.</p> <h2>Katakana via Shift</h2> <p>Holding Shift while typing converts the output to katakana instead of hiragana. Since the hiragana and katakana Unicode blocks share the same internal order, can can just offset the hiragana value by a certain amount to get katakana. Just like adding <code>('A' - 'a')</code> to an ASCII lowercase letter gives you the corresponding uppercase letter, adding an offset of <code>(L'ア' - L'あ')</code> code points to any hiragana maps it to katakana.</p> <p>Problem is we’re working with UTF-8, not code points directly. A two-way conversion is needed to do the offset in code point space:</p> <pre><code>// converts one UTF-8 hiragana character to katakana in place function to_katakana(char* kana) { // decode UTF-8 (the first byte will be unchanged) uint32_t codepoint = ((kana[1] &amp; 0b00111111) &lt;&lt; 6) | (kana[2] &amp; 0b00111111); // hiragana to katakana offset codepoint += (uint32_t)L'ア' - (uint32_t)L'あ'; // encode back to UTF-8 kana[1] = 0x80 | ((codepoint &gt;&gt; 6) &amp; 0b00111111); kana[2] = 0x80 | ( codepoint &amp; 0b00111111); }</code></pre> <h2>The typing feel</h2> <p>An inherent awkwardness in romaji input (<a href="https://leanrada.com/notes/baybayin_abugida_keyboard_qmk?ref=rss">and other syllable-based Roman input methods</a>) is you only know which kana to output once the next vowel is pressed. Buffering consonants until the next vowel makes the keyboard feel unresponsive. This is especially true when the input method lives in the hardware, not on the computer software.</p> <p>The solution is to emit the Roman letter immediately on each consonant keypress, giving instant feedback. When the vowel arrives, we backspace over the previous consonants and replace it with the final kana:</p> <pre><code>// in my keypress processing logic } else if (curr_cons) { tap_code(curr_keycode); // emit Roman letter immediately } else if (prev_cons &amp;&amp; curr_vowel) { syllable_t syllable = process_syllable(prev_prev_cons, prev_cons, curr_vowel); // delete Roman letter(s) while (syllable.backspaces-- &gt; 0) tap_code(KC_BACKSPACE); send_kana_unicode(...); }</code></pre> <p>For multi-letter combinations like <em>sh</em> or <em>tsu</em>, multiple backspaces are issued accordingly. If you pause mid-syllable for more than 2 seconds, the state resets, abandoning the buffered consonant, and the next keypress starts fresh.</p> <h2>The main looker upper</h2> <p>Finally, the <code>process_syllable()</code> function is what runs all of the above rules against the lookup table. Reading the code provides a nice overview of all the rules in this input method implementation:</p> <pre><code>// called when prev_keycode is a consonant and curr_keycode is a vowel // returns the identified syllable syllable_t process_syllable(void) { bool preprev_cons = is_consonant(preprev_keycode); syllable_t result; result.backspaces = 0; // three-letter combinations if (preprev_cons) { if (match("tsu")) { // つ result.backspaces += 2; result.consonant_kc = KC_T; result.vowel_kc = curr_keycode; result.extra_char_ptr = NULL; return result; } else if (match("dzu")) { // づ result.backspaces += 2; result.consonant_kc = KC_D; result.vowel_kc = curr_keycode; result.extra_char_ptr = NULL; return result; } // ひゃ,にゅ,きょ,... if (preprev_cons &amp;&amp; prev_keycode == KC_Y) { result.backspaces += 2; result.consonant_kc = preprev_keycode; result.vowel_kc = KC_I; result.extra_char_ptr = &amp;MAP[ROW_EXTRA_YOUON][vowel_offset(curr_keycode)]; return result; } // Map C+H and S+H to the CH/SH rows in the map if (preprev_keycode == KC_C &amp;&amp; prev_keycode == KC_H) { result.backspaces += 1; preprev_keycode = 0; // not a 'keycode' anymore, just an index prev_keycode = KC_A + ROW_EXTRA_CH; } else if (preprev_keycode == KC_S &amp;&amp; prev_keycode == KC_H) { result.backspaces += 1; preprev_keycode = 0; prev_keycode = KC_A + ROW_EXTRA_SH; } } // end three-letter combinations // じゃ,じゅ,じょ if (is_youon(prev_keycode, curr_keycode)) { result.backspaces += 1; result.consonant_kc = prev_keycode; result.vowel_kc = KC_I; result.extra_char_ptr = &amp;MAP[ROW_EXTRA_YOUON][vowel_offset(curr_keycode)]; return result; } // ファ,ヴァ,ジェ,... if (is_small_vowel(prev_keycode, curr_keycode)) { result.backspaces += 1; result.consonant_kc = prev_keycode; result.vowel_kc = get_base_vowel(prev_keycode, curr_keycode); result.extra_char_ptr = &amp;MAP[ROW_SMALL_VOWELS][vowel_offset(curr_keycode)]; return result; } // a regular syllable result.backspaces += 1; result.consonant_kc = prev_keycode; // must be consonant result.vowel_kc = curr_keycode; // must be vowel result.extra_char_ptr = NULL; return result; }</code></pre> <h2>Why not implement a software IME?</h2> <a href="https://leanrada.com/notes/baybayin_abugida_keyboard_qmk/?ref=rss#why-not-ime">Answered here.</a> <h2>Source code</h2> <a href="https://github.com/Kalabasa/qmk_firmware/blob/97bae401a89ac29bd4dba7acdc2da834e43e3e9c/keyboards/lily58/keymaps/Kalabasa/features/kana.c" target="_blank">My QMK fork on GitHub (GPL)</a>

2026/3/2
阅读更多

Cool URLs don’t change with snapshot testing

<p>I wrote automated tests to keep track of my site’s URLs and prevent links from breaking.</p> <p>When I reorganised my site, I changed a lot of URLs and broke some inbound links. Now that more and more sites have started linking to my site, I don’t want to inadvertently break links again.</p> <blockquote><a href="https://www.w3.org/Provider/Style/URI" target="_blank">Cool URIs don't change</a><cite>Tim Berners-Lee (1998)</cite></blockquote> <p>This post is NOT about <em>how</em> to implement <a href="https://thehistoryoftheweb.com/cool-urls-mean-something/" target="_blank">‘cool URLs’</a>. Too many ways to do that depending on the site’s setup and the site’s philosophy even. Rather, this post is about ensuring cool URLs — a <i>contract</i> to guarantee that published URLs continue to work. It doesn’t matter <em>how</em> your specific site implementation fulfil that contract.</p> <p>There already exists a contract: a <em>social contract</em> when you publish a webpage. Other people can link to your URL on their sites, and you can link to other people’s content on your site in return. And we expect these inbound and outbound links to work, otherwise the World Wide Web would be pretty boring to browse.</p> <p>A stronger contract is to treat URLs as a public API. Once published, it becomes an interface other people depend on. Changing it is a breaking change, just like changing a function signature.</p> <p>If it’s an API, can we perhaps run automated tests against it?</p> <h2>Testing URLs automatically</h2> <p>Your website could be a static site, a Java Spring Boot thing, or a PHP server, it doesn’t matter as long as it serves HTML over HTTP.</p> <p>Essentially, the test is to fetch each URL off a list of known URLs and check expected responses.</p> <pre><code>test.each([ ['/', 200], ['/about', 200], ['/about/', 200], ['/signin', 301, '/login'], ['/news', 302, '/en-US/news'], ['/api/ping', 204], ['/dashboard', 403], ['/secret', 404], ])('%s %i', async (path, status, location) =&gt; { const res = await fetch(base + path, { redirect: 'manual' }); expect(res.status).toBe(status); if (location) expect(res.headers.get('location')).toBe(location); });</code></pre> <p>But manually writing a test case for every URL would be a pain. Updating tests is also tedious. This is where snapshot testing comes in.</p> <h2>Snapshot testing</h2> <p>The easiest way to do this is via <a href="https://jestjs.io/docs/snapshot-testing" target="_blank">snapshot testing</a> (or <a href="https://ro-che.info/articles/2017-12-04-golden-tests" target="_blank">golden tests</a>).</p> <p>Snapshot testing takes a record of the system’s behaviour and saves that as a baseline or ‘golden file’ against which future versions of the system are compared.</p> <p>In my case, the snapshot is the set of URLs my site serves. A ‘snap’ involves a <a href="https://en.wikipedia.org/wiki/Web_crawler" target="_blank">crawler</a> gathering all the URLs and writing them to a text file. Unlike snapshot testing approaches that inline values in the test code, I prefer to keep this snapshot as a separate artifact so changes can be reviewed more easily.</p> <pre><code>/ /about/ /notes/ /wares/ /wares/pong-ai/ ...and so on... </code></pre> <p>Then when I make refactors or any code change, I can run the crawler again and compare the snapshots. Any difference to the baseline is treated as a breaking change unless explicitly approved.</p> <p>The interesting part is using git to turn that comparison into an enforced check.</p> <h2>Snapshot testing with git</h2> <p>With git, I don’t actually need to ‘write tests’ or use a testing framework.</p> <p>git is the testing framework.</p> <ul> <li>To run tests, run the crawler and check the <a href="https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---exit-code" target="_blank">exit code</a> of <code>git diff --quiet -- path/to/snapshot</code>. </li><li>To debug failures, <code>git diff</code> can show me the <span>expected</span> and <span>actual</span> values, just like a testing framework. </li><li>To approve changes to the baseline, I commit the snapshot file. </li></ul> <p>Preventing accidental changes is the real goal, so a <a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks" target="_blank">pre-commit hook</a> is set up to abort commits when the snapshot contains an unapproved change.</p> <pre><code># pre-commit ./tests/my-crawler &gt;git-hooks/pre-commit.log 2&gt;&amp;1 if ! git diff --quiet -- tests/url-snapshot; then echo "URL snapshot file changed. Commit aborted." exit 1 fi</code></pre> <p>With this, any change to URLs must be intentional, like with public APIs. It’s like URLs as versioned public API.</p> <p>If I wanted to restructure my site again, I now have a way to check how existing URLs would have been taken care of (e.g. redirected).</p> <p>As a bonus, this also helps prevent me from accidentally publishing draft posts (which I’ve done a few times)!</p> <h2>Other notes</h2> <ul> <li> It’s like Page Indexing tracking in Google Search Console. <figure> <img src="https://leanrada.com/notes/cool-urls-with-snapshot-testing/google-search-console.png?ref=rss" alt="Screenshot of Google Search Console showing indexed URLs" loading="lazy" width="904" height="360"> <figcaption>This used to tell me when a URL suddenly goes 404 or something.</figcaption> </figure> </li> <li> Status code can be included in the snapshot so it can explicitly keep track of unreachable URLs (which by definition, won’t be crawled). <code-block language="text"> <pre><code>200 / 200 /about/ 200 /notes/ 200 /wares/ 404 /portfolio/ ...and so on...</code></pre> </code-block> </li> <li>The crawler can even use the snapshot itself as starting points for the crawl, instead of starting from just the root <code>/</code>. This way, the snapshot does not forget URLs. Once published, a URL is either a 200 or a 404. It’s like bookmarking every page on your site.</li> <li>This could be extended to track other page properties as well, such as the canonical URL. Anything that is often overlooked or auto-generated that you might want to review before deploying.</li> </ul> <h2>Applicability to different kinds of websites (in order)</h2> <p>For static <strong>plain HTML</strong> sites like mine, it’s probably not going to be very useful. URLs change only when you move HTML files around. No surprises here. But since I tend to forget about things and refactor stuff for the fun of it, this will still be a good thing as a self-imposed rule for future me (<i>a contract with myself?</i>).</p> <box-note>I have broken the CSS on this site countless times due to CSS refactors. I’ve been meaning to do <em>visual</em> snapshot testing to keep refactors at bay but visual snapshot testing is noisy, not to mention slow. Definitely not a pre-commit thing.</box-note> <p>On the other hand, for sites made with <a href="https://en.wikipedia.org/wiki/Static_site_generator" target="_blank">static site builders</a>, explicitly reviewing changes to site structure could be better than relying on assumptions about the internal route-generation logic of the site generator. This is if you’re not already checking in the generated files in source control, at which point the URL snapshot is kinda redundant.</p> <p>For a complex site with <strong>servers</strong>, rewrite rules, frameworks, or proxies, a version of this snapshotting routine could be set up to help prevent unwanted changes, but it couldn’t be git-based anymore if you have database-driven or otherwise dynamic URLs.</p> <p>Sites based on client-side routing won’t work at all, unless the crawler is more advanced and can execute JS (hard).</p> <h2>Closing notes</h2> <p>Treats URLs as versioned, reviewable artifacts. Once published, they stop being an implementation detail and start being something you should consciously maintain.</p> <p>URL mappings are often treated as emergent properties of a web server (nested routes? wildcard routes?) or a site generator’s build system. An explicit exhaustive URL listing makes them more tangible.</p> <p>I wrote a lot about testing and APIs and philosophy but in the end it’s basically a crawler sitemap + git. :D</p>

2026/1/25
阅读更多

Implementing a Baybayin abugida input method on keyboard hardware

<p><em>For RSS readers: This article contains interactive content available on the <a href="https://leanrada.com/notes/baybayin_abugida_keyboard_qmk/?ref=rss">original post on leanrada.com</a>.</em></p> <p>I made an input method to write Baybayin, a syllable-based writing system, using Roman letters on a hardware keyboard. <small>Banner photo lifted from a <a href="https://www.reddit.com/r/MechanicalKeyboards/comments/fesl6p/gc60_x_baybayin_keycaps/" target="_blank">Reddit post</a> showing Baybayin keycaps. This post is about a real Baybayin keyboard though.</small></p> <h2>The writing system</h2> <p><a href="https://en.wikipedia.org/wiki/Baybayin" target="_blank">Baybayin</a> is an ancient writing system in which a consonant-vowel syllable is written as one letter — it is an <strong>abugida</strong>. By default, letters have an inherent vowel which is /a/. To indicate a different vowel, a dot is added above or below the letter.</p> <ol> <li><i baybay=""><ruby>ᜃ<rp> (</rp><rt>ka</rt><rp>)</rp></ruby></i>: The base letter on its own is the consonant plus the vowel <i>‘a’</i>. </li><li><i baybay=""><ruby>ᜃᜒ<rp> (</rp><rt>ki</rt><rp>)</rp></ruby></i>: A dot above makes it end with <i>‘i’</i> / <i>‘e’</i>. </li><li><i baybay=""><ruby>ᜃᜓ<rp> (</rp><rt>ku</rt><rp>)</rp></ruby></i>: A dot below makes it end with <i>‘u’</i> / <i>‘o’</i>. </li><li><i baybay=""><ruby>ᜃ᜕<rp> (</rp><rt>k</rt><rp>)</rp></ruby></i>: A slash cancels the vowel (it’s called a <a href="https://en.wikipedia.org/wiki/Virama" target="_blank"><strong>virama</strong></a>). </li></ol> <p>An example of a full word is <i baybay=""><ruby>ᜆ<rt>ta</rt>ᜆ᜕<rt>t</rt>ᜎᜓ<rt>lo</rt><rp>(tatlo)</rp></ruby></i>, which means the number three.</p> <box-note><strong>HTML tip:</strong> I’m using the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/ruby" target="_blank"><code>&lt;ruby&gt;</code></a> element to annotate Baybayin pronunciation in this post, similar to furigana for Japanese.</box-note> <h2>Input design</h2> <p>There are many ways to design an input method for this writing system on a keyboard. The most straightforward would be to have a key for each letter. Assign every possible letter-diacritic combination its own key. There’d be a total of 63 resulting letters, which is a bit too much for a keyboard. For comparison, the Latin alphabet A-Z only has 26 letters.</p> <p>Layer or modes could help, accessed by holding the <kbd>Shift</kbd> or <kbd>Fn</kbd> key — after all, the Latin alphabet actually has 52 distinct symbols, counting both upper and lowercase.</p> <figure> <img src="https://leanrada.com/notes/baybayin_abugida_keyboard_qmk/every-combination.png?ref=rss" loading="lazy" width="824" height="353"> <figcaption>In this design, pressing Shift would change the vowel endings. </figcaption></figure> <p>Or we could assign only the 17 base letters and the 3 vowel diacritics for a total of 20 keys for input. This would be a pretty efficient layout, and it’s the one used by Google’s mobile keyboard Gboard. Some logic on top of this would be required to combine diacritics and letters.</p> <figure> <img src="https://leanrada.com/notes/baybayin_abugida_keyboard_qmk/gboard.png?ref=rss" loading="lazy" width="1080" height="1028"> <figcaption>Gboard Baybayin layout </figcaption></figure> <p>However, both of the above would require learning a whole new keyboard layout, wasting years of Latin alphabet-based muscle memory. For me who primarily uses the Latin alphabet for typing, this is worth considering.</p> <p>Let’s say I wanted to type in romanised alphabet, like romaji for Japanese or pinyin for Chinese. What if the keyboard itself can be programmed to convert keystrokes directly into Baybayin outputs? Implementing this on the hardware presents a nice challenge; one keypress would no longer directly correspond to one character.</p> <h2>My solution</h2> <p>I implemented this on top of a <a href="https://qmk.fm/" target="_blank">QMK</a> firmware base, making use of its neat Unicode API. This runs my keyboard’s microcontroller.</p> <p>It’s a pretty basic solution, just a bunch of if-elses and variables. We don’t need a buffer or a trie or whatever data structure. It only needs to know about the last pressed key. <small>Well, it’s a buffer of length 1.</small></p> <pre><code>void on_key_press(uint16_t keycode) { bool curr_cons = is_consonant(keycode); bool prev_cons = is_consonant(prev_keycode); bool curr_vowel = !curr_cons; // special case for NG bool is_ng = prev_keycode == KEYCODE_N &amp;&amp; keycode == KEYCODE_G; // standalone vowel if (curr_vowel &amp;&amp; !prev_cons) { send_unicode(get_baybayin_unicode(keycode)); } // initial consonant else if (curr_cons &amp;&amp; !is_ng) { send_unicode(get_baybayin_unicode(keycode)); send_unicode("᜕"); // virama, because no vowel initially } // vowel after consonant else if (curr_vowel &amp;&amp; prev_cons) { backspace(); // remove the initial virama if (keycode == KEYCODE_I || keycode == KEYCODE_E) { send_unicode("ᜒ"); } else if (keycode == KEYCODE_U || keycode == KEYCODE_O) { send_unicode("ᜓ"); } // KEYCODE_A emits nothing since /a/ is the default vowel } // NG digraph else { // is_ng // replace previous N with the letter for NG backspace(); // remove virama backspace(); // remove ᜈ send_unicode("ᜅ"); send_unicode("᜕"); } prev_keycode = keycode; }</code></pre> <p>The code shown here is just the core loop for readability. Not shown is how to handle interference from other keys such as Backspace and Arrow keys and other housekeeping.</p> <h2>Housekeeping</h2> <p>Whenever a non-alphabet key is pressed, I just reset all state and skip further processing. This solves keyboard shortcuts: if I press <kbd>Ctrl</kbd> + <kbd>S</kbd> I want to save, not output <i baybay=""><ruby>ᜐ᜕<rt>s</rt></ruby></i>.</p> <p>This also solves a stale <code>prev_keycode</code>. When you press <kbd>Backspace</kbd> for example, <code>prev_keycode</code> is no longer accurate. A buffer would actually help here, but it’d unnecessarily complicate the code. Cursor movement keys <kbd>←</kbd>, <kbd>→</kbd>, or any other non-letter key for that matter, <kbd>1</kbd>, <kbd>.</kbd>, <kbd>F2</kbd>, etc.; all reset state.</p> <p>This cannot catch any state-invalidating actions that happen outside the keyboard, like selecting text with the mouse! A timeout takes care of these cases. After a short time without typing, state is reset.</p> <p>Essentially, we only want to convert into Baybayin when continuously typing sequences of letters. Otherwise, it should behave like a regular keyboard.</p> <p>The full working code is in <a href="https://github.com/Kalabasa/qmk_firmware/blob/2be68c4e8dc3977aaef1037d82d29ed8a59db903/keyboards/lily58/keymaps/Kalabasa/features/baybayin.c" target="_blank">my QMK fork on GitHub</a>.</p> <h2>Unicode encoding</h2> <p>The way the Unicode Consortium designed the encoding of the <a href="https://www.unicode.org/charts/nameslist/n_1700.html" target="_blank">Tagalog (Baybayin) Unicode block</a> made this solution pretty straightforward. The vowel-modifier diacritics are <strong>combining characters</strong>, which means they can just be output separately, right after the base character. There are no precomposed consonant-vowel symbols.</p> <pre><code>ᜆ + ᜕ = ᜆ᜕</code></pre> <p>The word processing application or text renderer on the computer is what actually renders that sequence of characters as one combined symbol. In a way, one keypress still corresponds to one output character, except for a few special cases.</p> <h2>Special case: N + G</h2> <p>One special case is the letter <i baybay="">ᜅ</i>, representing the consonant /ŋ/. In English this sound is represented by two letters <i>‘ng’</i>. This romanisation breaks the rule of 1 Roman consonant letter to 1 Baybayin base letter. For example, <i>‘ngiti’</i> (meaning ‘smile’) is written as <i baybay=""><ruby>ᜅᜒ<rt>ngi</rt>ᜆᜒ<rt>ti</rt></ruby></i>. This special case is solved by <em>backtracking:</em> sending a signal to the computer to erase the previous N.</p> <h2>Backtracking</h2> <p>To backtrack, we just send a virtual Backspace to the computer. Coming from the keyboard, it will be indistinguishable from a real Backspace keypress.</p> <p>Backtracking is a thing mainly because the virama (‘vowel-killer’) is always output after typing a consonant. This is because when the user presses a consonant, we don’t know yet whether they’re going to type a syllable or stop at that point. <strong>Since base letters have an inherent vowel, we must immediately kill the inherent vowel with a virama when typing consonants.</strong> Only the next key will decide — we can backtrack the virama by then.</p> <p>The following simulation illustrates how the keyboard-based solution handles backtracking:</p> <pre>Interactive content: <a href="https://leanrada.com/notes/baybayin_abugida_keyboard_qmk/?ref=rss">Visit the post to interact with this content.</a> Alternative name: Figure Alternative text: simulation of the input method</pre> <p>So pressing, say, the <kbd>M</kbd> key should not output just the base letter <i baybay=""><ruby>ᜋ<rp> (</rp><rt>ma</rt><rp>)</rp></ruby></i>, but the letter + virama <i baybay="">ᜋ᜕</i> at once. If you press <kbd>A</kbd> next, it would <em>actually output a Backspace</em>, erasing the virama and leaving <i baybay=""><ruby>ᜋ<rp> (</rp><rt>ma</rt><rp>)</rp></ruby></i> as intended. If you had pressed <kbd>I</kbd>, a Backspace is sent along with the I vowel diacritic <span>ᜒ</span>, producing <i baybay=""><ruby>ᜋᜒ<rp> (</rp><rt>mi</rt><rp>)</rp></ruby></i>.</p> <p>Because of the inherent vowel in most abugidas, a backtracking design seems to be used in other romanised input methods as well, such as <a href="https://anjal.net/" target="_blank">Anjal</a>, a romanised input method for Tamil.</p> <h2>Why not a buffer instead of backtracking?</h2> <p>Buffering keystrokes would remove the need to backtrack outputs, at the cost of responsiveness. In that setup, pressing a consonant would produce no glyph at all until the following key disambiguates the syllable and flushes the output. Loss of immediate visual feedback would make the keyboard feel unresponsive and breaks the rhythm of typing.</p> <b>Input buffering demonstration!</b><input placeholder="type syllables"> <pre>Interactive content: <a href="https://leanrada.com/notes/baybayin_abugida_keyboard_qmk/?ref=rss">Visit the post to interact with this content.</a> Alternative name: Inline script</pre> <p>One of the downsides of a hardware-based solution is that we can only communicate via keycodes<sup>1</sup>. A software-based input method <em>editor</em> (IME) doesn’t have these restrictions; it can easily <em>edit</em> the string, access the undo stack, get the currently selected text, and more. This would eliminate the need for aggressive state invalidation and timeouts. There are pros and cons.</p> <h2>Why not implement a software IME?</h2> <p>Because it’s more fun to hack on keyboard firmware!</p> <p>More seriously, implementing this on the hardware makes it portable. Not that I often need to write Baybayin, but I use multiple computers with a KVM switch. Software input methods are very OS- or application-dependent. (On Linux, it depends on even more things, such as your desktop environment, GTK or QT, X or Wayland, you name it.) <em>This</em> input method lives in <em>my</em> keyboard. I could plug my keyboard into any computer that supports arbitrary Unicode input<sup>1</sup> and not have to install or reimplement anything.</p> <box-note>Someone did <a href="https://inurwansah.my.id/2021/04/17/philippine-scripts-keyboard-for-linux/" target="_blank">make an input method for Baybayin</a> and other Philippine scripts for Linux (<a href="https://github.com/Ilhamkang/Phillippines-input-method" target="_blank">Github</a>).</box-note> <h2><sup>1</sup>How does a keyboard even output Unicode?</h2> <p>Technically, it can’t. It requires a certain understanding with the computer OS.</p> <p>You know the Alt codes in Windows where you can summon any character by their code number? The keyboard basically types that sequence of keystrokes in quick succession when we say ‘send unicode’. This abstraction is all done by QMK, supporting Windows, Linux, and Mac’s Unicode input method.</p> <p>To enter <code>U+1700</code> on Linux for example, the keyboard rapidly sends the following keystrokes in order: Hold <kbd>Ctrl</kbd>. Hold <kbd>Shift</kbd>. <kbd>U</kbd>. <kbd>1</kbd>. <kbd>7</kbd>. <kbd>0</kbd>. <kbd>0</kbd>. <kbd>Enter</kbd>. Release <kbd>Ctrl</kbd>. Release <kbd>Shift</kbd>. This outputs a <i baybay="">ᜀ</i>.</p> <p>With all of the above logic, when I type <kbd>T</kbd><kbd>A</kbd>, the actual keycodes sent are: <kbd>Ctrl</kbd><kbd>Shift</kbd><kbd>U</kbd><kbd>1</kbd><kbd>7</kbd><kbd>0</kbd><kbd>6</kbd><kbd>Enter</kbd><kbd>Ctrl</kbd><kbd>Shift</kbd><kbd>U</kbd><kbd>1</kbd><kbd>7</kbd><kbd>1</kbd><kbd>5</kbd><kbd>Enter</kbd><kbd>Backspace</kbd></p> <p>The same Unicode API from QMK powers <a href="https://leanrada.com/notes/my-personalised-keyboard?ref=rss">emoji buttons in my keyboard!</a></p> <h2>Can this be generalised to other abugidas?</h2> <p>Baybayin is a relatively simple script, so the implementation here might be on the simpler side. Complications might come in the form of consonant clusters or different ways to join glyphs.</p> <p>The implementation depends heavily on the Unicode encoding of the script. From what I’ve seen, many other abugidas have been designed in a similar way having vowels encoded as combining characters that modify base consonants. So there’s probably a common pattern of implementation.</p> <p>See also</p> <ul> <li><a href="https://www.unicode.org/events/utw/2023/documents/slides/Fixing-Input-Methods-for-Abugida-Scripts.pdf">Fixing Input Methods for Abugida Scripts (pdf, slides)</a> </li><li><a href="https://elangocheran.com/2022/02/14/redesigning-an-input-method-for-an-abugida-script/">Redesigning an Input Method for an Abugida Script</a> </li></ul> <h2>Source code</h2> <a href="https://github.com/Kalabasa/qmk_firmware/blob/2be68c4e8dc3977aaef1037d82d29ed8a59db903/keyboards/lily58/keymaps/Kalabasa/features/baybayin.c" target="_blank">My QMK fork on GitHub</a> <pre>Interactive content: <a href="https://leanrada.com/notes/baybayin_abugida_keyboard_qmk/?ref=rss">Visit the post to interact with this content.</a> Alternative name: Inline script</pre>

2026/1/5
阅读更多

Editing my website directly in the browser

<p>This website is editable!</p> <p>If you add <a href="https://leanrada.com/notes/editing-website-in-browser/?edit=&amp;ref=rss"><code>?edit</code></a> to any URL on this site, it activates <strong>edit mode</strong>. In this mode, you can edit text and apply basic formatting to any element.</p> <p>Of course, you won’t be able to save changes without having write access to my files, but I can. Using the Web <a href="https://developer.chrome.com/docs/capabilities/web-apis/file-system-access" target="_blank"><strong>File System API</strong></a>, it can update my local copy of the website’s source code.</p> <window-decor title="leanrada.com/notes/vanilla-web-rewrite/?edit"> <video src="https://leanrada.com/notes/editing-website-in-browser/preview.mp4?ref=rss" autoplay="" muted="" loop=""></video> </window-decor> <p>In short, I can edit my site via my site. In fact, this article was written this way!</p> <p>It’s still rough in some cases, but the solution is usable.</p> <h2>But why?</h2> <p>As usual, I made this mainly for fun. But having a WYSIWYG (what-you-see-is-what-you-get) editor for my posts has been a long time goal ever since I <a href="https://leanrada.com/notes/vanilla-web-rewrite?ref=rss" target="_blank">rewrote my site in vanilla HTML</a>.</p> <p>After I moved away from a static site generator, the need for an extra transformational build step disappeared, allowing the raw HTML source to be directly served in the client. This unlocked a path to source editing in the client! As well as other basic&nbsp;<a href="https://developer.chrome.com/docs/devtools/workspaces" target="_blank">things</a>&nbsp;for free.</p> <p>The HTML file can now act as the editing platform, in addition to being the coding and authoring medium, and of course, the publishing medium.</p> <img src="https://leanrada.com/notes/editing-website-in-browser/html-3.png?ref=rss" loading="lazy" width="450" height="300"> <p>We're sticking with standards with this one. I’ve noticed that this has been the tendency of my personal use of Web tech.</p> <prose-table> <table> <tbody><tr> <th>Instead of… </th><th>I use </th></tr><tr> <td>CMS </td><td>the file system </td></tr><tr> <td>frontmatter </td><td>read data already in the markup,<br> <code>&lt;meta&gt;</code>, <code>&lt;title&gt;</code>, <code>&lt;time&gt;</code>, <code>&lt;tag-chip&gt;</code>s </td></tr><tr> <td>WYSIWYG editor </td><td>editable HTML page </td></tr><tr> <td>Advanced editor (?) </td><td>text editor (edit HTML code) </td></tr><tr> <td>Widgets (??) </td><td>JavaScript </td></tr></tbody></table> </prose-table> <p>Anyway, these things don’t really impact visitors and readers of this site. It’s more about my writing and authoring experience. Thoughts can now go directly into the page the way it’s gonna be read.</p> <p>I already felt a bit of that improved flow when I removed the build step and added a <a href="https://leanrada.com/notes/simple-live-reload?ref=rss" target="_blank">live reload script</a>, minimising the development feedback loop. With direct editing, feedback loop is instant — no more switching between the text editor and the browser! It’s been extremely valuable during the proofreading and editing phase.</p> <h2>Content, editable</h2> <p>Browsers support editing HTML natively via the <code>contenteditable</code> attribute. Set this attribute on any element and it becomes editable. It’s a barebones way to get a WYSIWYG editor in HTML. This is what I’m using as of time of writing.</p> <p><code>contenteditable</code> formatting options are limited though, and browser implementations vary. Not a problem for now.</p> <p>Whether <code>contenteditable</code> was good <a href="https://medium.engineering/why-contenteditable-is-terrible-122d8a40e480" target="_blank">or terrible</a>&nbsp;was a minor detail in the bigger picture. The real problem was the <em>Document Object Model</em>…</p> <h2>DOM vs source</h2> <p>When I said no build step I kinda lied. You see, there's still an inherent compilation step that happens <em>inside the browser</em>. This step is the parsing of HTML into the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction" target="_blank"><strong>Document Object Model</strong></a>, or the <strong>DOM</strong>. The “DOM” essentially are the elements that you actually interact with on a webpage.</p> <box-note>Technically, the DOM is the interface to the elements themselves. For general purposes, <em>the interface is the abstraction</em>.</box-note> <p>The contenteditable editor edits the DOM, not the HTML source code itself. I needed to somehow save the corresponding DOM edits into the source. It was not very straightforward.</p> <img src="https://leanrada.com/notes/editing-website-in-browser/dom.png?ref=rss" loading="lazy" width="450" height="300"> <p>HTML to DOM conversion is one way. DOM to source HTML would be another thing. And no, <code>innerHTML</code>/<code>outerHTML</code> is not the exact inverse operation.</p> <p>For example, simply saving <code>html.outerHTML</code> to a file won’t work, because <code>outerHTML</code> (or any DOM introspection method, for that matter) takes a snapshot of the current state of the DOM, including <em>dynamically rendered elements</em>. This would break interactive elements and custom elements in the source, as the rendered elements would incorrectly overwrite the original markup.</p> <p>Saving the generated <code>outerHTML</code> directly would also mess up any manual formatting that I make in the source code, collapsing whitespaces and forcing HTML entities. <small>(I don’t use Prettier; it produces invalid HTML)</small></p> <h2>Solution: source patching</h2> <p>So I made an algorithm to patch the source according to changes made in the DOM. What it does is listen for changes in the DOM tree, and tries to identify the ‘smallest’ textual patch needed to update the source.</p> <p>It starts with the <strong><a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver" target="_blank">MutationObserver API</a></strong>. MutationObserver lets the code respond to changes in the DOM. For each change, the API provides the relevant node(s), as well as the two surrounding unchanged siblings.</p> <box-note> <details> <summary>Btw, the MutationObserver API has been <a href="https://github.com/whatwg/dom/issues/1105" target="_blank">somewhat</a> <a href="https://github.com/whatwg/dom/issues/1111" target="_blank">unwieldy</a>.</summary> <p>It only provides coarse details about each change, not like a detailed diff. Also, a single user interaction (say, pressing the Enter key) could cause multiple mutations to fire (the Enter key causes a &lt;p&gt; to be inserted, then a &lt;br&gt; inside the p). Some mutations also end up cancelling each other in the same callback or across multiple callbacks (e.g. span that is added and removed immediately). The exact order and amount of effects also vary by browser.</p> <p>My workaround was to collapse multiple mutations into one logical change per resulting subtree.</p> </details> </box-note> <p>The idea is to update the relevant part of the source without changing the rest. To do this, it finds two anchor points around the mutation. These two points must remain stable throughout the mutation in both DOM and source. Only the content between them are replaced.</p> <p>So for each change, the steps are:</p> <ol> <li>Find the unchanged element before the change. </li><li>Find the unchanged element after the change. </li><li>Use their <em>positions in source</em> as boundaries. </li><li>Splice the source <em>between the two boundaries</em> with the updated HTML. </li></ol> <img src="https://leanrada.com/notes/editing-website-in-browser/splice.png?ref=rss" loading="lazy"> <p>Then there’s the subproblem of mapping the position of the boundary elements in the source code. Extracting the position of any given DOM element in the source code is a bit more involved:</p> <ol> <li>Get the element’s ‘address’, or its path in the DOM (unique steps from the root to the element). </li><li>Scan the source HTML, tag by tag, keeping track of your current ‘address’ by identifying opening and closing tags. </li><li>Compare the current tag’s path-in-source against the element’s path-in-DOM. </li><li>If paths match, then the current tag corresponds to the element! </li></ol> <card-box> <details> <summary> <code>getPositionInSource</code> pseudocode </summary> <code-block> <pre><code>/** Gets the position of the given Element in the source code */ getPositionInSource(element: Element, source: string) { // Get the DOM path of the target domPath: Element = []; while (element) { domPath.unshift(element); element = element.parentElement; } let startIndex, endIndex; sourcePath = []; // match all HTML tags for (tag of source.matchAll(/&lt;\/?[a-z0-9-]+/gi)) { // Note: does not handle void elements and optional end tags if (isOpenTag(tag)) { sourcePath.push(tag); if (matchPath(domPath, sourcePath)) { // Source path and DOM path matches! // This is the target element's opening tag startIndex = tag.index; } } else { // is a closing tag if (matchPath(domPath, sourcePath)) { // Source path and DOM path matches! // This is the target element's closing tag endIndex = source.indexOf("&gt;", tag.index); return { startIndex, endIndex }; } sourcePath.pop(); } } } </code></pre> </code-block> <p>Handling void elements (e.g. <code>&lt;input&gt;</code>) and optional end tags (e.g. <code>&lt;li&gt;</code>s with no end tags) require a bit more logic to keep the current path accurate, but the main idea remains the same.</p> </details> </card-box> <p>The main algorithm can be described as basically a contextual find-and-replace. Instead of searching for the text to be replaced, it finds the surrounding text and replaces the content in the middle.</p> <p>This preserves the source code’s original formatting and avoids overwriting dynamic elements! Yay!</p> <p>There’s still a lot of room for improvement. My current issue is that it’s converting nearby HTML entities to rendered versions (e.g. <code>'&amp;hellip;'</code> to <code>'…'</code>). I think I’d need to implement a more fine-grained editing within an individual text node to solve that.</p> <p>There are probably a bunch more edges cases that I haven’t discovered, but hey it works well enough to write the text you’re reading!</p> <p>You can check out the <a href="https://leanrada.com/tools/edit.js" target="_blank">code for the edit mode</a> for more details! Note: This script was made specifically for myself and my site.</p> <h2>Thoughts</h2> <p>The algorithm doesn’t have to be perfect; I can always fall back to editing the source if certain things don’t work. A good thing about this is that I can switch to either method any time because in the end I'm working on the same HTML file.</p> <p>When I first imagined this solution, I thought I would only be using it to do the final edits just before publishing a post, but here I am actually writing a whole post.</p> <p>I can now imagine a workflow where I write the main text in the browser, then go into the HTML to insert media, mark up code blocks, custom elements, and other formatting. And of course, I’d use a code editor to code the interactive sections that I enjoy adding to my posts.</p> <p>In the future, think I’d like to add more formatting options and UI for inserting code blocks, media, lists, headings, etc. Maybe use <a href="https://quilljs.com/" target="_blank">Quill</a> or something?</p> <p>The top thing I would love to have is inserting hyperlinks. During final passes on a post, I like to sprinkle hyperlinks on interesting terms, even if just to link the <a href="https://www.wikipedia.org/" target="_blank">Wikipedia</a> entry.</p> <p>I’ll post about any improvements I make along the way. This could be a series! I mean, it already kind of is:</p> <ol> <li><a href="https://leanrada.com/notes/vanilla-web-rewrite?ref=rss">Rewriting my site in vanilla web</a> </li><li><a href="https://leanrada.com/notes/simple-live-reload?ref=rss">Simple live reload for developing static sites</a> </li><li>📍<a href="https://leanrada.com/notes/editing-website-in-browser?ref=rss"><strong>(this post)</strong></a> </li><li>[future formatting feature?] </li><li>[edit CSS? JS?] </li></ol>

2025/9/1
阅读更多

Making my GitHub heatmap widget

<p><em>For RSS readers: This article contains interactive content available on the <a href="https://leanrada.com/notes/github-heatmap-widget/?ref=rss">original post on leanrada.com</a>.</em></p> <p>This post is about how I made the GitHub heatmap widget on my site.</p> <p>Here’s the raw, live WebComponent <code>&lt;gh-contribs&gt;</code> by the way:</p> <card-box> <pre>Interactive content: <a href="https://leanrada.com/notes/github-heatmap-widget/?ref=rss">Visit the post to interact with this content.</a> Alternative name: Gh Contribs</pre> </card-box> <h2>Scraping the data</h2> <p>First, I had to scrape the heatmap data. I don’t know if there’s a proper API, but I found an endpoint that renders an HTML partial of what I wanted.</p> <p>As far as I know, the GitHub website today works using partial HTMLs to update its UI without reloading the whole page. I think this endpoint populates the contribution graph section of the profile page.</p> <p>The endpoint that returns a user’s contribution graph is <a href="https://github.com/users/Kalabasa/contributions" target="_blank"><code>https://github.com/users/{username}/contributions</code></a>. I presume the user has to have contribution stats public. This undocumented API could also break at any time. 😬</p> <figure> <img src="https://leanrada.com/notes/github-heatmap-widget/contributions-html.png?ref=rss" loading="lazy" width="932" height="741"> <figcaption>The response HTML for my github.com/users/Kalabasa/contributions</figcaption> </figure> <p>Loading this endpoint gives you an unstyled piece of HTML containing an HTML table of contribution data and other UI. The table cells are invisible because of the lack of styles! When embedded in the profile page, it inherits the appropriate styling in context.</p> <img alt="styled contribution table" src="https://leanrada.com/notes/github-heatmap-widget/contributions-styled.png?ref=rss" loading="lazy" width="741" height="204"> <p>The first column is the weekday label, and the rest of the cells seem to represent a single day each. The data is encoded in the HTML that presents the data! This reminds me of <a href="https://htmx.org/essays/hateoas/" target="_blank">Hypermedia as the Engine of Application State</a>. html = data.</p> <pre><code>&lt;tbody&gt; &lt;tr style="height: 10px"&gt; &lt;td class="ContributionCalendar-label" style="position: relative"&gt; &lt;span class="sr-only"&gt;Monday&lt;/span&gt; &lt;span aria-hidden="true" style="clip-path: None; position: absolute; bottom: -3px"&gt; Mon &lt;/span&gt; &lt;/td&gt; &lt;td tabindex="0" data-ix="0" aria-selected="false" aria-describedby="contribution-graph-legend-level-2" style="width: 10px" data-date="2024-08-05" id="contribution-day-component-1-0" data-level="2" role="gridcell" data-view-component="true" class="ContributionCalendar-day"&gt; &lt;/td&gt; &lt;td tabindex="0" data-ix="1" aria-selected="false" aria-describedby="contribution-graph-legend-level-1" style="width: 10px" data-date="2024-08-12" id="contribution-day-component-1-1" data-level="1" role="gridcell" data-view-component="true" class="ContributionCalendar-day"&gt; &lt;/td&gt; &lt;td tabindex="0" data-ix="2" aria-selected="false" aria-describedby="contribution-graph-legend-level-2" style="width: 10px" data-date="2024-08-19" id="contribution-day-component-1-2" data-level="2" role="gridcell" data-view-component="true" class="ContributionCalendar-day"&gt; &lt;/td&gt; <!--...--></code></pre> <p>What I was looking for here was the <code>data-level</code> attribute on each cell. It contains a coarse integer value that indicates the activity level for the day.</p> <p>Coupled with the <code>data-date</code> attribute, it became rather easy to scrape this data! Instead of keeping track of columns and rows, I just go through each <code>data-date</code> and <code>data-level</code> as a (date,level) data point.</p> <p>Here’s my parse function using <a href="https://cheerio.js.org/" target="_blank">cheerio</a>, a jQuery clone for Node.js.</p> <pre><code>const res = await fetch( "https://github.com/users/Kalabasa/contributions"); let data = parseContribs(await res.text()); /** * Parses a GitHub contribution calendar HTML string and extracts contribution data. * * @param {string} html - The HTML string containing the GitHub contribution calendar. * @returns {{ date: Date, level: number }[]} Array of contribution objects with date and activity level. * @throws {Error} If the contribution calendar table cannot be found in the HTML. */ function parseContribs(html) { const ch = cheerio.load(html); const chTable = ch("table.ContributionCalendar-grid"); if (!chTable.length) throw new Error("Can't find table."); const chDays = chTable.find("[data-date]"); const data = chDays .map((_, el) =&gt; { const chDay = ch(el); const date = new Date(chDay.attr("data-date")); const level = parseInt(chDay.attr("data-level"), 10); return { date, level }; }) .get(); return data; }</code></pre> This data is scraped at regular intervals, reformatted into a grid, and saved into a compact JSON format for later consumption and rendering by the WebComponent on the client. <pre><code>// gh-contribs.json [ [1,2,1,2,2,1,1], [0,1,0,2,0,1,0], [1,1,1,1,1,1,0], [0,1,0,0,1,1,0], [0,0,1,0,0,0] ]</code></pre> <h2>Rendering the data</h2> <p>The reason why the data is reformatted into a grid like that is to make the rendering logic straightforward. The data is structured so that it can be directly converted into HTML without thinking in dates and weeks that are in the original data.</p> <p>Here are the current JSON and WebComponent side by side. Each row in the data gets directly rendered as a column in the component.</p> <auto-grid> <code-block language="js"> <pre><code> <pre>Interactive content: <a href="https://leanrada.com/notes/github-heatmap-widget/?ref=rss">Visit the post to interact with this content.</a> Alternative name: Inline script</pre> </code></pre> </code-block> <pre>Interactive content: <a href="https://leanrada.com/notes/github-heatmap-widget/?ref=rss">Visit the post to interact with this content.</a> Alternative name: Gh Contribs</pre> </auto-grid> <p>As such, <code>&lt;gh-contribs&gt;</code>’s initialisation logic is really simple:</p> <pre><code>const contribs = await fetch( "path/to/gh-contribs.json" ).then((res) =&gt; res.json()); let htmlString = ""; for (const col of contribs) { for (const level of col) { htmlString += html`&lt;div data-level="${level}"&gt;${level}&lt;/div&gt;`; } } this.innerHTML = htmlString;</code></pre> Add some CSS and it’s done: <pre><code>gh-contribs { display: grid; grid-auto-flow: column; grid-template-rows: repeat(7, auto); gap: 12px; div { position: relative; width: 18px; height: 18px; background: #222c2c; color: transparent; &amp;::after { content: ""; position: absolute; inset: 0; background: #54f8c1; } &amp;[data-level="0"]::after { opacity: 0; } &amp;[data-level="1"]::after { opacity: 0.3; } &amp;[data-level="2"]::after { opacity: 0.6; } &amp;[data-level="3"]::after { opacity: 1; } } }</code></pre> <h2>Why not use the original HTML?</h2> <p>Why not just embed the contributions HTML from GitHub? Slice the relevant <code>&lt;tr&gt;</code>s and <code>&lt;td&gt;</code>s…? Why parse the original HTML table, convert it to JSON, then render it as HTML again?</p> <p>The main reason to do [HTML → JSON → HTML] is to remain flexible. As you know, that endpoint is undocumented. Also, depending on the HTML structure of the original is risky. Risk of breakage, risk of unwanted content, etc.</p> <p>This way, I can change how I get the data without refactoring the WebComponent. I could go [<strong>GitHub API</strong> → JSON → HTML] or [<strong>local git script</strong> → JSON → HTML] or whatever.</p> <p>It also works the other end. I actually rewrote this widget recently (from statically-generated HTML into a WebComponent) without having to change the scraper script or the JSON data structure.</p> <h2>Final touches</h2> <p>The WebComponent renders just the grid itself for flexibility. This let me use it in different ways, like with an icon and heading as in the home page.</p> <card-box> <h4> <img src="https://leanrada.com/icons/github.png?ref=rss" alt="" loading="lazy" width="16" height="16"> my github<br>heatmap </h4> <pre>Interactive content: <a href="https://leanrada.com/notes/github-heatmap-widget/?ref=rss">Visit the post to interact with this content.</a> Alternative name: Gh Contribs</pre> </card-box> <p>Here’s the <a href="https://leanrada.com/components/gh-contribs/gh-contribs.js?ref=rss" target="_blank">source code for this WebComponent</a> if you’re interested.</p>

2025/8/4
阅读更多

Vibecoding Lima Gang, web-based map visualization

<p>I made <strong><a href="https://kalabasa.github.io/lima/" target="_blank">Lima Gang</a></strong>, a map-based visualization, over a weekend using <strong>‘vibecoding’</strong>.</p> <img src="https://leanrada.com/notes/vibecoding-lima-gang/screenshot.png?ref=rss" alt="Screenshot" loading="lazy" width="2800" height="1400"> <h2>Lima what?</h2> <p><strong><i>lima</i></strong> means <em>five</em> in several languages particularly in island nations in Southeast Asia and the Pacific. These are part of the Austronesian language family that spans several seas from Madagascar to Indonesia, to Taiwan, to Hawaii.</p> <figure> <img src="https://leanrada.com/notes/vibecoding-lima-gang/austronesian.png?ref=rss" loading="lazy" width="960" height="388"> <figcaption>Extent of Austronesian peoples. They must’ve been good with boats. Source: <a href="https://commons.wikimedia.org/wiki/File:Chronological_dispersal_of_Austronesian_people_across_the_Pacific.svg" target="_blank">Wikimedia</a></figcaption> </figure> <p>The <strong>Lima Gang</strong> is a meme based on the fact that the word <i>lima</i> seemed to have survived intact, echoed in various tongues, and now somehow uniting people across vast oceans.</p> <p>This is more than a meme, Unicode even gave recognition to the Lima Gang by including its gang sign as an official emoji: 🖐🏽</p> <p>Jokes aside, I’m posting this to share a vibecoding experience.</p> <h2>Vibecoding</h2> <p>This small one-off app that I knew almost exactly how to make is a perfect case for some <a href="https://en.wikipedia.org/wiki/Vibe_coding" target="_blank">vibecoding</a> practice. My initial thoughts were, it was going to be mostly boilerplate HTML code, some standard map UI code, and some standard visualization code. There are hundreds of map visualization web pages just like this, nothing too novel here. I used ChatGPT to generate about 80-90% of the code.</p> <p>First, I had it generate the basic UI. It gave me a basic Leaflet-based map.</p> <figure> <img src="https://leanrada.com/notes/vibecoding-lima-gang/basic-ui.png?ref=rss" loading="lazy" width="800" height="400"> <figcaption>Basic Leaflet map.</figcaption> </figure> <p>It’s good to start with the basics. From my brief experience, it’s better to implement small chunks at a time than bigger integrated pieces.</p> <p>I didn’t like the look of OpenStreetMap, and I know <a href="https://carto.com/basemaps" target="_blank">Carto</a> has some nice-looking maps, so I asked ChatGPT to switch to Carto.</p> <p>I remember having to manually tweak the tile layer API URL to get the specific style that I wanted: <code>{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png</code>. This was the first time I looked into any documentation for the components that I was using.</p> <img src="https://leanrada.com/notes/vibecoding-lima-gang/carto.png?ref=rss" alt="Carto base map" loading="lazy" width="800" height="400"> <p>Skipping the friction of going through documentation to get started with an API or a library was great for building up momentum at the start of a project, and I think LLMs are good with that initial scaffolding, but looking up documentaton is still necessary at some point.</p> <h2>Getting the data</h2> <p>The main source of data is <a href="https://en.wiktionary.org/wiki/lima" target="_blank">this Wiktionary entry on <strong>‘lima’</strong></a>.</p> <p>I asked ChatGPT to create a script that scrapes this page. It should look for all entries where ‘lima’ means ‘five’, and list the corresponding languages in JSON format. A couple of iterations later, it gave me a Python script that uses the Wiktionary REST API.</p> <code><pre># ... url = "https://en.wiktionary.org/api/rest_v1/page/definition/lima" response = requests.get(url) data = response.json() language_map = {} for section, entries in data.items(): for entry in entries: language = entry["language"] entry_definition_htmls = entry.get("definitions", []) entry_definitions = {extract_definition(d.get("definition", "")) for d in entry_definition_htmls} entry_definitions.discard(None) if language not in language_map: language_map[language] = {"name": language, "definitions": set()} language_map[language]["definitions"].update(entry_definitions) # ...</pre></code> <p>Again, not having to look these APIs up was a great time-saver. And I don’t usually code in Python, so it would’ve taken me time to review sets and list comprehensions otherwise.</p> <p>I did have to go into the REST API and the Python code because of increasingly complex requirements, like getting secondary meanings, and massaging the data. ChatGPT wasn’t really able to dive in and iterate on these more complex requirements.</p> <p>In the end I got this JSON file, the list of languages where ‘lima’ means 5:</p> <code><pre>[ { "name": "Agutaynen" }, { "name": "Bariai" }, { "name": "Cia-Cia", "otherMeanings": ["hand"] }, { "name": "Dibabawon Manobo" }, { "name": "East Futuna" }, { "name": "Gela" }, { "name": "Hiligaynon", "otherMeanings": ["hand", "handle", "lime"] }, // ... ]</pre></code> <p>I still needed to map this data geographically, so I asked ChatGPT to assign geographic IDs for the purpose of rendering these on the map. It’s basically a fill-in-the-blank exercise for ChatGPT. I think even spreadsheet apps have this kind of AI autocomplete now.</p> <prose-table> <table> <tbody><tr> <th>Language </th><th>Geo code </th></tr><tr> <td>Agutaynen </td><td><strong>PHL</strong> [<em>Philippines</em>] </td></tr><tr> <td>Bariai </td><td><strong>PNG</strong> [<em>Papua New Guinea</em>] </td></tr><tr> <td>Cia-Cia </td><td><strong>IDN</strong> [<em>Indonesia</em>] </td></tr><tr> <td>Dibabawon Manobo </td><td>? </td></tr><tr> <td>East Futuna </td><td> </td></tr><tr> <td>Gela </td><td> </td></tr><tr> <td>Hiligaynon </td><td> </td></tr></tbody></table> </prose-table> <p>This is where I leaned a bit into the vibecoding vibes, because I have no scalable way to verify this data. I can verify code and logic, but I’m no linguist. I relied on ChatGPT’s knowledge and/or search capabilities to map the languages to where they are spoken.</p> <p>To instill some confidence, I created a new thread and asked it to ‘fact-check’ the data it generated in a previous instance.</p> <img src="https://leanrada.com/notes/vibecoding-lima-gang/fact-check-query.png?ref=rss" alt="Screenshot telling ChatGPT to fact-check the JSON" loading="lazy" width="640" height="209"> <p>Of course, it proceeded to ignore my second instruction in its very first bullet point, but it actually gave me a good number of issues to double-check. The <a href="https://www.ethnologue.com/language/khl/" target="_blank">Lusi</a> issue was hallucinated, but the rest seemed reasonable.</p> <img src="https://leanrada.com/notes/vibecoding-lima-gang/fact-check-answer.png?ref=rss" alt="Incorrect or questionable entries: Brunei Malay — GeoCode should be 'BRN' (correct). Lusi — No known language 'Lusi' in PNG; likely incorrect or misspelled. Muduapa — No known language by this name in Indonesia. Papora — Extinct Taiwanese language, but rarely used; valid but obscure. Old Javanese — Historical language, not currently spoken; 'IDN' is reasonable. Hawaiian — GeoCode 'HI' is U.S. state code, not ISO 3166-1 alpha-3. Should be 'USA'. Tokelauan — GeoCode 'TKL' is not ISO 3166-1 alpha-3 (correct ISO code is 'TKL' for Tokelau but it's an exception; often 'TKL' is not officially ISO-3)." loading="lazy" width="865" height="358"> <p>The Hawaiian and Tokelauan cases were actually good points to raise, and I had to add special handling for those later.</p> <box-note> <p>I think a safer way to go about it is to use the LLM to map the language name to language code, then use a database to lookup country codes from language codes.</p> <p>This way the LLM would just be doing a syntactic transformation, which it should be good at, leaving the accuracy-sensitive job of identifying the appropriate country to a deterministic database lookup.</p> <p>Maybe there’d be no need for an LLM at all if I got the language code from the initial Wiktionary scrape in the first place. 🤔</p> </box-note> <p>I also asked ChatGPT to fetch GeoJSON data needed to render these geographic areas on the map. It hallucinated some GeoJSON datasets like <code>raw.githubusercontent.com/david/world/b1b8704/data/country.geojson</code>. In the end, I had to search for some of the GeoJSON data myself.</p> <figure> <img src="https://leanrada.com/notes/vibecoding-lima-gang/geojson.png?ref=rss" loading="lazy" width="800" height="400"> <figcaption>Map with GeoJSON layer highlighting the identified areas.</figcaption> </figure> <p>There were a few special cases regarding geographic areas as hinted at earlier. These are the state of Hawaii and some territories of New Zealand. These aren’t countries themselves, and it didn’t make sense to highlight the whole USA to represent the Hawaiian language, or all of New Zealand for the Tokelauan language.</p> <p>ChatGPT didn’t like that I was using both countries and states and territories in the same dataset. I had to implement these special cases mostly manually, only giving really small isolated instructions to ChatGPT.</p> <h2>Interactivity</h2> <p>I asked it to generate popups on hover per geographic area to show the data in more detail. It added code using Leaflet tooltips. Another cool API I didn’t know beforehand. Overall pretty straightforward.</p> <figure> <img src="https://leanrada.com/notes/vibecoding-lima-gang/tooltips.png?ref=rss" loading="lazy" width="800" height="400"> <figcaption>Map tooltips on hover.</figcaption> </figure> <p>That’s most of it. I did the CSS polishing bits and mobile responsiveness. I don’t feel like ChatGPT would have an <em>eye</em> for this as a <em>language</em> model. Maybe it’s multimodal now, but I still don’t trust it for visual things.</p> <box-note>In the end, what happened wasn’t technically pure vibecoding as in the original definition. It started out that way, but I saw some pretty wrong code and and took the wheel at some point. Unfortunately I don’t possess the blissful gift of ignorance.</box-note> <h2>Opinions</h2> <p>From this experience, I can say that ChatGPT (or LLMs in general?) can get you started on tiny apps really quickly. That initial phase of writing boilerplate, finding the right libraries, and setting it all up can be automated away. But, it’s still not quite reliable enough to go bigger or deeper without proper direction. I don’t think this is a groundbreaking observation, many others have noted similar strengths and weaknesses of LLMs for coding.</p> <p>To be fair, I didn’t use Cursor or any kind of coding agent. I have some experience with Cursor at work, and while it is significanly more capable than the general chat interface, still I doubt it could create the whole app in a single ask. It likely would require the same directed iteration in chunks as with a general chat interface, just maybe faster and in slightly bigger chunks.</p> <p>I think for successful AI-assisted coding, you must have some kind of an implementation plan in mind, a breakdown of components and subcomponents to implement and integrate (just like in real life). In my case, I didn’t really have an explicit plan, but I had a sense of which bite-sized chunks to implement and iterate on.</p> <p>I’ve seen some practices where the agent generates that plan itself, and iterates on it in small chunks. Like making a todo list for itself then completing that. I think that would actually go further, and with less supervision. But you still have to review and know what it’s doing at any point so you can course-correct in case it goes off track, which happens often.</p> <p>In conclusion: I guess you have to know what you’re doing.</p> <p></p>

2025/6/13
阅读更多

My own RSS reader

<p>I started making my own RSS reader webapp (<a href="https://reedybear.bearblog.dev/getting-started-with-feed-readers/" target="_blank">a what reader?</a>). It can be accessed at <a href="https://leanrada.com/tools/rss?ref=rss" target="_blank"><code>/tools/rss/</code></a>. Currently it’s hardcoded to just load my own site feed <code>leanrada.com/rss.xml</code> and that’s totally fine.</p> <p>I made this primarily to test how my own posts get rendered in <strong>‘reader mode’</strong>.</p> <h2>Reader mode</h2> <p>You see, I like to incorporate interactive elements in my posts. However, custom elements and JS are scrubbed by RSS reader apps, ultimately resulting in a clean but confusing and broken experience. Even custom CSS which I use to format some graphs and illustrations are lost.</p> <p>The same could be said for the <i>Reading mode</i> feature offered by most full-fledged web browsers today, if not their add-ons. It’s a feature that strips ‘distracting’ elements away. My own RSS reader actually uses <strong><a href="https://github.com/mozilla/readability" target="_blank">the same library</a></strong> used by Mozilla Firefox’s <i>Reader View</i>.</p> <figure> <img src="https://leanrada.com/notes/my-own-rss-reader/ss.png?ref=rss" alt="Screenshot of my RSS reader" loading="lazy" width="1280" height="768"> <figcaption>My barebones RSS reader showing a reader mode transformation of posts.</figcaption> </figure> <p>But many of my posts are not meant to be just for <em>reading</em>. I want my posts to be interactive and playable. A <em>reading</em> mode transformation is inherently incompatible. Still, I wanted to provide an RSS feed. I had to compromise.</p> <p>An easy way out would be to omit the post body in the feed, maybe put in a summary instead, and just link to the original URL like what other feeds do. The <a href="https://feeds.bbci.co.uk/news/rss.xml" target="_blank">BBC news feed</a> does this, for example. But that’s boring.</p> <h2>An alt text kind of way</h2> <p>I went with an alt text kind of way. That is, all interactive content are replaced by alternative plain text in the feed body.</p> <fixed-grid> <figure> <img src="https://leanrada.com/notes/my-own-rss-reader/before-reading.png?ref=rss" loading="lazy" width="704" height="832"> <figcaption>Original content</figcaption> </figure> <figure> <img src="https://leanrada.com/notes/my-own-rss-reader/after-reading.png?ref=rss" loading="lazy" width="704" height="832"> <figcaption>Converted content</figcaption> </figure> </fixed-grid> <p>Right now it’s pretty crude — my HTML to RSS converter scans custom tags in the markup, and replaces them with the tag name in sentence case. So <code>&lt;some-demo&gt;&lt;/some-demo&gt;</code> would turn into <i>Some demo</i>.</p> <p>It also tries to look for <code>alt</code> and <code>aria-label</code> attributes if present.</p> <p>It’s not really a complete solution since it doesn’t cover other stuff like plain DOM-based JS stuff (non-WebComponents). I don’t think RSS readers will start running third-party JS or WebComponents anytime soon, so this is not going to be a temporary solution either.</p> <p>The idea for the RSS reader is that it will help me continuously test these conversions, and remind myself to polish the HTML to RSS solution.</p> <figure> <img src="https://leanrada.com/notes/my-own-rss-reader/alt-prelude.png?ref=rss" alt="Quote: For RSS readers: This article contains interactive content available on the original post on leanrada.com." loading="lazy" width="755" height="71"> <figcaption>Prelude prepended to the post body when there is interactive content in the post.</figcaption> </figure> <p>Beyond just a testing tool, this RSS reader is, well, an RSS reader!</p> <h2>It is a feed reader!</h2> <p>Now that I’ve made an actual RSS reader, it’d be trivial to add other feeds and use this as my main reader app! I’ve always wanted to make my own feed reader.</p> <p>I’m currently using an online service <a href="https://www.inoreader.com/" target="_blank">InoReader</a> to subscribe to and read around <strong>123 feeds</strong> in total. I could potentially switch to my own reader…? I should probably start with OPML import functionality. It would be a nice project, at least.</p> <p>Actually, more than a feed reader, I’ve always wanted to make my own personal front page containing not just RSS feeds, but also social media posts and other stuff I follow all in one place. So many project ideas… 🤔</p>

2025/5/15
阅读更多

Simple live reload for developing static sites

<p>When developing my website, I’m using a simple client-side script to <strong>automatically reload the page</strong> whenever I make a change to the source files.</p> <p>Since it’s not coupled to any particular backend, I could continue using <code>python3 -m http.server -d ./site/</code> or whatever local web server I wanted and it would still work. I could clone this repo on a new machine and get going with only preinstalled programs: a text editor, a browser, and a Python (or whatever) HTTP server. And live reload should<sup>*</sup> just work.</p> <prose-bleed> <video autoplay="" muted="" loop="" src="https://leanrada.com/notes/simple-live-reload/demo.mp4?ref=rss" alt="demo video" loading="lazy"></video> </prose-bleed> <p>Here’s the code (39 lines):</p> <pre><code>let watching = new Set(); watch(location.href); new PerformanceObserver((list) =&gt; { for (const entry of list.getEntries()) { watch(entry.name); } }).observe({ type: "resource", buffered: true }); function watch(urlString) { if (!urlString) return; const url = new URL(urlString); if (url.origin !== location.origin) return; if (watching.has(url.pathname)) return; watching.add(url.pathname); console.log("watching", url.pathname); let lastModified, etag; async function check() { const res = await fetch(url, { method: "head" }); const newLastModified = res.headers.get("Last-Modified"); const newETag = res.headers.get("ETag"); if ( (lastModified !== undefined || etag !== undefined) &amp;&amp; (lastModified !== newLastModified || etag !== newETag) ) { location.reload(); } lastModified = newLastModified; etag = newETag; } setInterval(check, 1000); }</code></pre> <h2>Chuck it into your HTML</h2> <pre><code>&lt;script src="https://kalabasa.github.io/simple-live-reload/script.js"&gt;&lt;/script&gt;</code></pre> <p>It should just work! ✨</p> <p><small><sup>*</sup>Check the <a href="https://github.com/Kalabasa/simple-live-reload/blob/master/README.md" target="_blank">README</a> for more details.</small></p> <h2>How it works, in a nutshell</h2> <ul> <li>Start <a href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver" target="_blank"><code>PerformanceObserver</code></a> to watch loaded URLs </li><li>Poll <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods/HEAD" target="_blank"><code>HEAD</code></a> metadata </li><li>Check <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Last-Modified" target="_blank"><code>Last-Modified</code></a> and <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/ETag" target="_blank"><code>ETag</code></a> </li></ul> <p><strong>PerformanceObserver</strong> — This class was intended to measure performance of things like network requests. But in this case it was repurposed to record requested URLs so we can watch them for changes. This includes lazy-loaded resources, and resources not in the markup (e.g. imported JS modules)!</p> <p><strong>HEAD</strong> — Upon recording a requested URL, start polling the URL with <code>HEAD</code> HTTP requests to get resource metadata. Frequent polling should be fine if you’re using a local server which is what I would expect for development. The response returned by <code>HEAD</code> contains metadata useful for determining when to refresh.</p> <p><strong>Last-Modified</strong> and <strong>ETag</strong> — These are the headers used to indicate when the underlying resource has changed. The script triggers a <code>location.reload()</code> when any of these change.</p> <prose-bleed> <img src="https://leanrada.com/notes/simple-live-reload/flow.png?ref=rss" alt="flowchart showing the steps" loading="lazy" width="1056" height="387"> </prose-bleed> <h2>Story time</h2> <p>I was directly inspired by <strong><a href="https://livejs.com/" target="_blank">livejs</a></strong> (2004), which polls headers as well. However, it has not been updated for modern browsers. Instead of watching network requests, it scans the markup for <code>&lt;script&gt;</code> and <code>&lt;link&gt;</code> (CSS) resources.</p> <p>In fact, I’ve been using livejs for a long while. I’ve never been fond of the other solutions which require integration with your local filesystem, via extra programs that you install and run. They always seem to run slow or take up lots of resources, and sometimes choke if there are errors.</p> <p>I’m planning to make a fine-grained version of this module. Simple live reloading is fine, but a more advanced <em>hot</em> reloading that doesn't always refresh the whole page would be great. For the modern web, the advanced version must be able to <a href="https://github.com/WICG/webcomponents/issues/820" target="_blank">hot reload WebComponents</a> in place, and do other fun stuff. I wonder if it’s even possible. 🤔</p> <p><strong>GitHub repo: <a href="https://github.com/Kalabasa/simple-live-reload" target="_blank">Kalabasa/simple-live-reload</a></strong></p>

2025/4/18
阅读更多

Language evolves, so we must too. On generative art

<p>A year ago, I stood by the statement that <a href="https://leanrada.com/notes/ai-art-not-generative-art?ref=rss">AI Art is not Generative Art</a>, arguing that the term ‘generative art’ is an art movement in itself that is separate from AI art.</p> <p>Safe to say, it has been a losing battle.</p> <p>Even the converse, <em>“generative art is not necessarily AI art”</em>, would be met by confusion from most people. ‘Generative’ has been equated to AI.</p> <figure> <img src="https://leanrada.com/notes/language-evolves-generative-art/fidenza.jpg?ref=rss" loading="lazy" width="1000" height="1200"> <figcaption>Fidenza, a generative artwork system, by Tyler Hobbs</figcaption> </figure> <h2>Exhibition</h2> <img src="https://leanrada.com/notes/language-evolves-generative-art/sadly-yes.png?ref=rss" loading="lazy" width="654" height="140"> <img src="https://leanrada.com/notes/language-evolves-generative-art/when-we-talk.png?ref=rss" loading="lazy" width="805" height="101"> <img src="https://leanrada.com/notes/language-evolves-generative-art/the-other-day.png?ref=rss" loading="lazy" width="718" height="118"> <p><a href="https://www.reddit.com/r/generative/comments/1hks3sg/has_the_word_generative_been_hijacked_by_ai/" target="_blank">r/generative - Has the word 'generative' been hijacked by AI?</a></p> <p>I think it's time to move on to a different label. Words are for communication, and if a word results in failure of communication, we should either agree on a common definition (we can’t) or just use better word.</p> <h2>So, what are the other words for this art?</h2> <box-note>If you haven’t read the mentioned <a href="https://leanrada.com/notes/ai-art-not-generative-art?ref=rss" target="_blank">prequel post</a>, you may not understand what “art” I’m taliking about. There’s one example above, but <a href="https://leanrada.com/art?ref=rss" target="_blank">here’s more</a>.</box-note> <p>Fortunately, there are already well-established terms to describe the medium:</p> <ul> <li>Procedural art </li><li>Algorithmic art </li><li>Creative coding </li></ul> <p>It’s just a matter of choosing one and leaving the now-loaded ‘generative’ term behind.</p> <p>By the process of elimination, I think ‘procedural art’ is the best term to use. Let me explain each of the other options.</p> <h2>Why not Algorithmic</h2> <p>‘Algorithmic art’ is a top candidate, for sure, and many others have <a href="https://monokai.com/articles/algorithmic-art-as-a-subset-of-generative-art" target="_blank">opted for it in the exodus from ‘generative’</a>. Algorithmic art and procedural art are pretty much synonymous, so why or why not?</p> <p><strong>Pros:</strong> Unlike generative art, algorithmic art doesn’t have the unfortunate connotation from sharing a word with ‘generative AI’.</p> <p><strong>Cons:</strong> While ‘algorithmic art’ doesn’t immediately invoke visions of generative AI, the term ‘algortihm’ under the broad definition of a ‘set of instructions’ would still include machine learning models.</p> <p>In fact, the word ‘algorithm’ has also entered commonplace usage via <strong>‘social media algorithms’</strong>, which are yet another application of machine learning and statistics. In this sense, it is used interchangeably with the recommender system used to construct personalised content feeds. <em>“The algorithm”</em> is a black box, and the word <a href="https://www.technologyreview.com/2021/02/26/1020007/what-is-an-algorithm/" target="_blank">suggests a system beyond understanding</a>. This connotation is not in the spirit of procedural art, where the procedures are authored knowingly.</p> <p>Another distinction is that while an algorithm is usually defined as a process that takes some <strong>input</strong> and processes it into an <strong>output</strong>, most procedural art programs don’t need inputs at all. They create things from scratch, from maths, from chaos. <small>OK, technically random seeds would be considerend input. But on a conceptual level, no inputs!</small></p> <h2>Why not Creative Coding</h2> <p>First of all, it’s such a vague phrase. Why not ‘dynamic programming’? 😉</p> <p>Anyway, that’s not the main point.</p> <p>Procedural art has grown beyond <em>just coding</em>. I would even say most procedural artists don't use code, but nodes and graphs. The majority of these procedural art would be used in video games and movie VFX.</p> <p>Instead of coding, one would use <a href="https://pixieditor.net/blog/2024/07/12/devlog5#nodes" target="_blank">node-based programming</a> connecting <strong>nodes</strong> that represent operations, building up a <strong>graph</strong> to produce a complex image or even a 3D model.</p> <figure> <img src="https://leanrada.com/notes/language-evolves-generative-art/nodes.jpg?ref=rss" alt="Node graph and its output" loading="lazy" width="1100" height="910"> <figcaption><a href="https://shahriyarshahrabi.medium.com/procedural-chinese-landscape-painting-in-geometry-nodes-blender-3d-6417403430e6" target="_blank">Shahriar Shahrabi - Procedural Chinese Landscape Painting in Geometry Nodes</a></figcaption> </figure> <p>And it’s not just for industrial VFX purposes. It’s also used mainly for multimedia, interactive, and installation art.</p> <figure> <img src="https://leanrada.com/notes/language-evolves-generative-art/sydney1_0.jpg?ref=rss" alt="Sydney Opera House with a projected image on it" loading="lazy" width="640" height="320"> <figcaption>Audiovisual projection by <a href="https://derivative.ca/community-post/obscura-digital-turns-sydney-opera-house-inside-out-outside/60793" target="_blank">Obscura Digital using TouchDesigner, a node-based software</a>. (<a href="https://www.youtube.com/watch?v=YKFxkqPdsoI" target="_blank">Picture from YouTube Symphony Orchestra</a>)</figcaption> </figure> <p>So... yeah this genre of art, is not coding.</p> <h2>OK, then why Procedural Art</h2> <p>Because the ‘procedure’, the ‘how’ is the core feature of procedural art.</p> <p>In procedural art, we describe precisely <strong>how</strong> the artwork is made. And it’s far from describing to a chatbot in plain English. The how is well-defined and understood. Every shape, stroke, their positions, colours, ranges, constraints, rules of interaction, etc., are all described in some precise way. Sometimes we go down to individual pixel level as in a shader. Sometimes at a more geometric level, like in turtle or vector graphics. Sometimes on a mathematical level, like fractals. Sometimes it’s even a simulation with emergent properties!</p> <p>Procedural art is more about the <em>process</em>, watching things unfold and emerge, rather than the final output that algorithms are obsessed about.</p> <p>Procedural art is about the interaction of rules in interesting ways, regardless of whether the rules were written in code, or connected as a graph, or wired in redstone dust.</p> <p>Finally, <em>procedural art is not AI art</em>. [Insert same exact argument as <a href="https://leanrada.com/notes/ai-art-not-generative-art?ref=rss" target="_blank">AI Art is not Generative Art</a> but with a different name, here]. For funsies, here’s a quick guide: <em>In AI art, you describe <strong>what</strong> the artwork should be. In procedural art, you describe <strong>how</strong> the artwork could be.</em></p> <h2>What now</h2> <p>Language evolves because of general usage. See: <em>AI, <a href="https://www.vice.com/en/article/cryptocurrency-are-not-crypto-bitcoin/" target="_blank">crypto</a>, cyber, generative</em>. As a fellow user of language, I must evolve my languaging as well — starting with this website.</p> <p>I have updated the following pages to either disambiguate or even completely replace mentions of “generative art”:</p> <ul> <li>/art/</li> <li>/wares/dimensions/</li> </ul> <p>For the record, I’m not rejecting the other perfectly acceptable terms like algorithmic art and creative coding. My art is still algorithmic, and it’s the result of creative coding. I’m just moving away from ‘generative’.</p> <p>I’m sure we’ll settle on a universal term soon. Language evolves, after all.</p> <h2>Appendix</h2> <figure> <img src="https://leanrada.com/notes/language-evolves-generative-art/search-time.png?ref=rss" loading="lazy" width="1122" height="424"> <figcaption>Searches for “generative art” over time</figcaption> </figure> <figure> <img src="https://leanrada.com/notes/language-evolves-generative-art/i-like.png?ref=rss" alt="Note saying: I like your algorithmic art" loading="lazy" width="402" height="115"> <figcaption>A visitor has left me this note, which made me rethink</figcaption> </figure>

2025/4/3
阅读更多

Minimal CSS-only blurry image placeholders

<p>Here’s a CSS technique that produces blurry image placeholders (LQIPs) without cluttering up your markup — <em>Only a single custom property needed!</em></p> <card-box> <code-block language="html"> <pre><code>&lt;img src="…" style="--lqip:192900"&gt;</code></pre> </code-block> <p>The custom property above gives you this image:</p> <p> </p><div></div> <p></p> <label> Try changing the property’s value <strong>(WARNING: FLASHING)</strong> <input type="range" min="-999999" max="999999"> </label> <script> { const card = document.currentScript.parentElement; const input = card.querySelector("input"); const code = card.querySelector("code"); const preview = card.querySelector("div"); let currentValueStr = "192900"; let targetCode = null input.addEventListener("input", event => { if (!targetCode) { targetCode = Array.from(code.querySelectorAll("span")).filter(el => el.textContent.includes(currentValueStr)).slice(-1)[0] ?? code; } const lqip = Number(event.currentTarget.value); // use this page's lqip to avoid breakage if I ever update the scheme preview.style.setProperty("--my-lqip", lqip); targetCode.innerHTML = targetCode.innerHTML.replace(currentValueStr, lqip); currentValueStr = String(lqip); }); } </script> </card-box> <p>Granted, it’s a <em>very blurry</em> placeholder especially in contrast to other leading solutions. But the point is that it’s minimal and non-invasive! No need for wrapper elements or attributes with long strings of data, or JavaScript at all.</p> <box-note>Note for RSS readers / ‘Reader’ mode clients: This post makes heavy use of CSS-based images. Your client may not support it.</box-note> <h2>Example images</h2> <lqip-examples> <img src="https://leanrada.com/notes/css-only-lqip/gallery/aj-McsNra2VRQQ-unsplash.jpg?ref=rss" width="300" height="199"> <img src="https://leanrada.com/notes/css-only-lqip/gallery/henry-co-3JFVNo4ukKQ-unsplash.jpg?ref=rss" width="300" height="200"> <img src="https://leanrada.com/notes/css-only-lqip/gallery/tienko-dima-uYoVf9I6ANI-unsplash.jpg?ref=rss" width="300" height="196"> <a href="https://leanrada.com/notes/css-only-lqip/javascript:(()=%3E%7Blet%20s;(s=document.getElementById('lqip-debug'))?s.remove%28%29%3A%28%28s=document.createElement%28%27style%27%29%29.innerHTML%3D%60%5Bstyle*%3D%27--my-lqip%3A%27%5D%3Anot%28%3Ahover%29%7Bobject-position%3Acalc%28infinity*1px%29%21important%7D%60%2Cs.id%3D%27lqip-debug%27%2Cdocument.head.append%28s%29%29%7D%29%28%29&amp;ref=rss"> Toggle images </a> </lqip-examples><card-box> <a href="https://leanrada.com/notes/css-only-lqip/gallery?ref=rss" target="_blank">Check out the LQIP gallery for examples!</a> </card-box> <h2>Survey of LQIP approaches</h2> <p>There have been many different techniques to implement LQIPs (low quality image placeholders), such as a very low resolution WebP or JPEG (<a href="https://engineering.fb.com/2015/08/06/android/the-technology-behind-preview-photos/" target="_blank">beheaded JPEGs</a> even), optimised SVG shape placements (<a href="https://github.com/axe312ger/sqip" target="_blank">SQIP</a>), and directly applying a discrete cosine transform (<a href="https://blurha.sh/" target="_blank">BlurHash</a>). Don’t forget good old progressive JPEGs and interlaced PNGs!</p> <figure> <img src="https://leanrada.com/notes/css-only-lqip/solid-colour.png?ref=rss" alt="image gallery with solid colour placeholders" width="1368" height="832"> <figcaption>Canva and Pinterest use solid colour placeholders.</figcaption> </figure> <p>At the other end of the spectrum, we have low tech solutions such as a simple solid fill of the image’s average colour.</p> <p>Pure inline CSS solutions have the advantage rendering immediately — even a <code>background-image: url(…a data URL)</code> would be fine!</p> <figure> <img src="https://leanrada.com/notes/css-only-lqip/gradient-css.png?ref=rss" alt="image gallery with gradient placeholders" width="1368" height="832"> <figcaption><a href="https://github.com/fraser-hemp/gradify" target="_blank">Gradify</a> generates linear-gradients that very roughly approximate the full image.</figcaption> </figure> <p>The big disadvantage of pure CSS approaches is that you typically litter your markup with lengthy inline styles or obnoxious data URLs. My handcoded site with no build step would be extra incompatible with this approach!</p> <pre><code>&lt;!-- typical gradify css --&gt; &lt;img width="200" height="150" style=" background: linear-gradient(45deg, #f4a261, transparent), linear-gradient(-45deg, #e76f51, transparent), linear-gradient(90deg, #8ab17d, transparent), linear-gradient(0deg, #d62828, #023047); "&gt; </code></pre> <p><strong><a href="https://blurha.sh/">BlurHash</a></strong> is a solution that minimises markup by compressing image data into a <strong>short base-83 string</strong>, but decoding and rendering that data requires additional JS… </p> <pre><code>&lt;!-- a blurhash markup --&gt; &lt;img width="200" height="150" src="…" data-blurhash="LEHV6nWB2yk8pyo0adR*.7kCMdnj"&gt;</code></pre> <figure> <img src="https://leanrada.com/notes/css-only-lqip/blurhash.png?ref=rss" width="276" height="171"> <figcaption>BlurHash example</figcaption> </figure> <p>Is it possible to decode a blur hash in CSS instead?</p> <h2>Decoding in pure CSS</h2> <p>Unlike BlurHash, we can’t use a string encoding because there are very few if any string manipulation functions in CSS (2025), so strings are out.</p> <p>In the end, I came up with my own hash / encoding, and the <b>integer</b> type was the best vessel for it.</p> <p>The usual way to encode stuff in a single integer is by <a href="https://en.wikipedia.org/wiki/Bit_manipulation" target="_blank"><strong>bit packing</strong></a>, where you pack multiple numbers in an integer as bits. Amazingly, we can unpack them in pure CSS!</p> <p>To unpack bits, all you need is bit shifting and bit masking. <strong>Bit shifting</strong> can be done by division and floor operations — <code>calc(x / y)</code> and <code>round(down,n)</code> — and <strong>bit masking</strong> via the modulo function <code>mod(a,b)</code>.</p> <pre><code>* { /* Example packed int: */ /* 0b11_00_001_101 */ --packed-int: 781; --bits-9-10: mod(round(down, calc(var(--packed-int) / 256)), 4); /* 3 */ --bits-7-8: mod(round(down, calc(var(--packed-int) / 64)), 4); /* 0 */ --bits-4-6: mod(round(down, calc(var(--packed-int) / 8)), 8); /* 1 */ --bits-0-3: mod(var(--packed-int), 8); /* 5 */ }</code></pre> <p>Of course, we could also use <code>pow(2,n)</code> instead of hardcoded powers of two.</p> <p>So, a <strong>single CSS integer value</strong> was going to be the encoding of the “hash” of my CSS-only blobhash (that’s what I’m calling it now). But how much information can we pack in a single CSS int?</p> <h2>Side quest: Limits of CSS values</h2> <p>The spec doesn’t say anything about the allowed range for int values, leaving the fate of my shenanigans to browser vendors.</p> <p>From my experiments, apparently you can only use integers from <strong>-999,999 up to 999,999</strong> in custom properties before you lose precision. Just beyond that limit, we start getting values rounded to tens — 1,234,56<del>7</del> becomes 1,234,56<ins>0</ins>. Which is weird (precision is counted in decimal places!?), but I bet it’s due to historical, Internet Explorer-esque reasons.</p> <p>Anyway, within the range of [-999999, 999999] there are <strong>1,999,999</strong> values. This meant that with a single integer hash, almost two million LQIP configurations could be described. To make calculation easier, I reduced it to the nearest power of two down which is 2<sup>20</sup>.</p> <card-box> <code>2<sup>20</sup> = 1,048,576 &lt; 1,999,999 &lt; 2,097,152 = 2<sup>21</sup></code> </card-box> <p><strong>In short, I had 20 bits of information to encode the CSS-based LQIP hash.</strong></p> <box-note><strong>Why is it called a “hash”?</strong> Because it’s a mapping from an any-size data to a fixed-size value. In this case, there are an infinite number of images of arbitrary sizes, but only 1,999,999 possible hash values.</box-note> <h2>The Scheme</h2> <p>With only 20 bits, the LQIP image must be a very simplified version of the full image. I ended up with this scheme: a single base colour + 6 brightness components, to be overlaid on top of the base colour in a 3×2 grid. A rather extreme version of <a href="https://en.wikipedia.org/wiki/Chroma_subsampling" target="_blank">chroma subsampling</a>.</p> <img src="https://leanrada.com/notes/css-only-lqip/scheme.png?ref=rss" alt="illustration of encoded components" width="600" height="500"> <p>This totals <strong>9 numbers</strong> to pack into the 20-bit integer:</p> <p>The <strong>base colour</strong> is encoded in the <strong>lower 8 bits</strong> in the <a href="https://en.wikipedia.org/wiki/Oklab_color_space" target="_blank">Oklab colour space</a>. 2 bits for luminance, and 3 bits for each of the a and b coordinates. I’ve found Oklab to give subjectively balanced results, but RGB should work just as well.</p> <p>The <strong>6 greyscale components</strong> are encoded in the <strong>higher 12 bits</strong> — 2 bits each.</p> <p>An offline script was created to compress any given image into this integer format. The script was quite simple: Get the average or dominant colour — there are a lot of libraries that can do that — then resize the image down to 3×2 pixels and get the greyscale values. <a href="https://github.com/Kalabasa/leanrada.com/blob/7b6739c7c30c66c771fcbc9e1dc8942e628c5024/main/scripts/update/lqip.mjs#L118-L159" target="_blank">Here’s my script.</a></p> <box-note>I even tried a <a href="https://blog.4dcu.be/programming/2020/01/12/Genetic-Art-Algorithm.html" target="_blank">genetic algorithm</a> to optimise the LQIP bits, but the fitness function was hard to establish. Ultimately, I would’ve needed an offline CSS renderer for this to work accurately. Maybe a future iteration could use some headless Chrome solution to automatically compare real renderings of the LQIP against the source image.</box-note> <p>Once encoded, it’s set as the value of <code>--lqip</code> via the style attribute in the target element. It could then be decoded in CSS. Here’s the actual code I used for decoding:</p> <pre><code>[style*="--lqip:"] { --lqip-ca: mod(round(down, calc((var(--lqip) + pow(2, 19)) / pow(2, 18))), 4); --lqip-cb: mod(round(down, calc((var(--lqip) + pow(2, 19)) / pow(2, 16))), 4); --lqip-cc: mod(round(down, calc((var(--lqip) + pow(2, 19)) / pow(2, 14))), 4); --lqip-cd: mod(round(down, calc((var(--lqip) + pow(2, 19)) / pow(2, 12))), 4); --lqip-ce: mod(round(down, calc((var(--lqip) + pow(2, 19)) / pow(2, 10))), 4); --lqip-cf: mod(round(down, calc((var(--lqip) + pow(2, 19)) / pow(2, 8))), 4); --lqip-ll: mod(round(down, calc((var(--lqip) + pow(2, 19)) / pow(2, 6))), 4); --lqip-aaa: mod(round(down, calc((var(--lqip) + pow(2, 19)) / pow(2, 3))), 8); --lqip-bbb: mod(calc(var(--lqip) + pow(2, 19)), 8);</code></pre> <p>Before rendering the decoded values, the raw number data values need to be converted to CSS colours. It’s fairly straightforward, just a bunch linear interpolations into colour constructor functions.</p> <pre><code>/* continued */ --lqip-ca-clr: hsl(0 0% calc(var(--lqip-ca) / 3 * 100%)); --lqip-cb-clr: hsl(0 0% calc(var(--lqip-cb) / 3 * 100%)); --lqip-cc-clr: hsl(0 0% calc(var(--lqip-cc) / 3 * 100%)); --lqip-cd-clr: hsl(0 0% calc(var(--lqip-cd) / 3 * 100%)); --lqip-ce-clr: hsl(0 0% calc(var(--lqip-ce) / 3 * 100%)); --lqip-cf-clr: hsl(0 0% calc(var(--lqip-cf) / 3 * 100%)); --lqip-base-clr: oklab( calc(var(--lqip-ll) / 3 * 0.6 + 0.2) calc(var(--lqip-aaa) / 8 * 0.7 - 0.35) calc((var(--lqip-bbb) + 1) / 8 * 0.7 - 0.35) ); }</code></pre> <card-box> <strong>Time for another demo!</strong> <label> Try different values of <code>--lqip</code> to decode <input type="range" min="-999999" max="999999"> </label> <p> <code></code> </p> <script> { const card = document.currentScript.parentElement; const input = card.querySelector("input"); const preview = card.querySelector(".lqip-unpack").parentElement; let currentValueStr = "-721311"; render(Number(currentValueStr)); input.addEventListener("input", event => { const lqip = Number(event.currentTarget.value); render(lqip); currentValueStr = String(lqip); }); function render(lqip) { preview.style.setProperty("--my-lqip", lqip); }; } </script> You can see here how each component variable maps to the LQIP image. E.g. the <code>cb</code> value corresponds to the relative brightness of the top middle area. <em>Fun fact: The above preview content is implemented in pure CSS!</em> </card-box> <h2>Rendering it all</h2> <p>Finally, rendering the LQIP. I used multiple <strong>radial gradients</strong> to render the greyscale components, and a flat base colour at the bottom.</p> <pre><code>[style*="--lqip:"] { background-image: radial-gradient(50% 75% at 16.67% 25%, var(--lqip-ca-clr), transparent), radial-gradient(50% 75% at 50% 25%, var(--lqip-cb-clr), transparent), radial-gradient(50% 75% at 83.33% 25%, var(--lqip-cc-clr), transparent), radial-gradient(50% 75% at 16.67% 75%, var(--lqip-cd-clr), transparent), radial-gradient(50% 75% at 50% 75%, var(--lqip-ce-clr), transparent), radial-gradient(50% 75% at 83.33% 75%, var(--lqip-cf-clr), transparent), linear-gradient(0deg, var(--lqip-base-clr), var(--lqip-base-clr)); }</code></pre> <p>The above is a simplified version of the full renderer for illustrative purposes. <small>The real one has doubled layers, smooth gradient falloffs, and blend modes.</small></p> <p>As you might expect, the radial gradients are arranged in a 3×2 grid. You can see it in this interactive deconstructor view!</p> <card-box> <strong>LQIP deconstructor!</strong> <label> Reveal the individual layers using this slider! <input type="range" min="0" max="100" value="0"> Change the <code>--lqip</code> value, <input type="range" min="-999999" max="999999"> </label> <div></div> <script> { const card = document.currentScript.parentElement; const [revealInput, lqipInput] = card.querySelectorAll("input"); const preview = card.querySelector(".lqip-reveal"); let currentValueStr = "-747540"; render(Number(currentValueStr)); lqipInput.addEventListener("input", event => { const lqip = Number(event.currentTarget.value); render(lqip); currentValueStr = String(lqip); }); revealInput.addEventListener("input", event => { preview.style.setProperty("--reveal", event.currentTarget.value / 100); }); function render(lqip) { preview.style.setProperty("--my-lqip", lqip); }; } </script> </card-box> <p>These radial gradients are the core of the CSS-based LQIP. The position and radius of the gradients are an important detail that would determine how well these can approximate real images. Besides that, another requirement is that these individual radial gradients must be seamless when combined together.</p> <p>I implemented smooth gradient falloffs to make the final result look seamless. It took special care to make the gradients extra smooth, so let’s dive into it…</p> <h2>Bilinear interpolation approximation with radial gradients</h2> <p>Radial gradients use linear interpolation by default. Interpolation refers to how it maps the in-between colours from the start colour to the end colour. And linear interpolation, the most basic interpolation, well…</p> <figure> <interpolation-example></interpolation-example> <figcaption>CSS radial-gradients with linear interpolation</figcaption> </figure> <p>It doesn’t look good. It gives us these hard edges (highlighted above). You could almost see the elliptical edges of each radial gradient and their centers.</p> <p>In real raster images, we’d use <a href="https://harmoniccode.blogspot.com/2011/04/bilinear-color-interpolation.html" target="_blank">bilinear interpolation</a> at the very least when scaling up low resolution images. <a href="https://blog.demofox.org/2015/08/15/resizing-images-with-bicubic-interpolation/" target="_blank">Bicubic interpolation is even better.</a></p> <p>One way to simulate the smoothness of bilinear interpolation in an array of CSS radial-gradients is to use <strong>‘quadratic easing’</strong> to control the gradation of opacity.</p> <p>This means the opacity falloff of the gradient would be smoother around the center and the edges. Each gradient would get feathered edges, smoothening the overall composite image.</p> <prose-bleed> <fixed-grid> <figure> <interpolation-example></interpolation-example> <figcaption> <strong>CSS radial-gradients:</strong> Quadratic interpolation <em>(touch to see edges)</em> </figcaption> </figure> <figure> <interpolation-example></interpolation-example> <figcaption> <strong>CSS radial-gradients:</strong> Linear interpolation <em>(touch to see edges)</em> </figcaption> </figure> <figure> <img src="https://leanrada.com/notes/css-only-lqip/interpolation-bilinear.png?ref=rss" width="900" height="600"> <figcaption>Image: Bilinear interpolation</figcaption> </figure> <figure> <img src="https://leanrada.com/notes/css-only-lqip/interpolation-bicubic.png?ref=rss" width="900" height="600"> <figcaption>Image: Bicubic interpolation</figcaption> </figure> <figure> <img src="https://leanrada.com/notes/css-only-lqip/interpolation-pixels.png?ref=rss" width="3" height="2"> <figcaption>Image: Your browser’s native interpolation</figcaption> </figure> <figure> <img src="https://leanrada.com/notes/css-only-lqip/interpolation-pixels.png?ref=rss" width="3" height="2"> <figcaption>Image: No interpolation</figcaption> </figure> </fixed-grid></prose-bleed> <p>However, CSS gradients <a href="https://github.com/w3c/csswg-drafts/issues/1332" target="_blank">don’t support nonlinear interpolation of opacity yet as of writing</a> (not to be confused with colour space interpolation, which browsers do support!). The solution for now is to add more points in the gradient to get a smooth opacity curve based on the quadratic formula.</p> <pre><code>radial-gradient( &lt;position&gt;, rgb(82 190 240 / 100%) 0%, rgb(82 190 204 / 98%) 10%, rgb(82 190 204 / 92%) 20%, rgb(82 190 204 / 82%) 30%, rgb(82 190 204 / 68%) 40%, rgb(82 190 204 / 32%) 60%, rgb(82 190 204 / 18%) 70%, rgb(82 190 204 / 8%) 80%, rgb(82 190 204 / 2%) 90%, transparent 100% )</code></pre> <figure> <img src="https://leanrada.com/notes/css-only-lqip/interpolation-graph.png?ref=rss" width="1024" height="768"> <figcaption>The quadratic interpolation is based on two quadratic curves (parabolas), one for each half of the gradient — one upward and another downward.</figcaption> </figure> <p>The quadratic easing blends adjacent radial gradients together, mimicking the smooth bilinear (or even bicubic) interpolation. It’s almost like a fake blur filter, thus achieving the ‘blur’ part of this BlurHash alternative.</p> <card-box> <a href="https://leanrada.com/notes/css-only-lqip/gallery?ref=rss">Check out the gallery for a direct comparison to BlurHash.</a> </card-box><lqip-examples> <img src="https://leanrada.com/notes/css-only-lqip/gallery/karsten-winegeart-613pTZEFf2U-unsplash.jpg?ref=rss" width="300" height="208"> <img src="https://leanrada.com/notes/css-only-lqip/gallery/fahrul-azmi-Q1l1ofdVYl4-unsplash.jpg?ref=rss" width="225" height="300"> <img src="https://leanrada.com/notes/css-only-lqip/gallery/esma-melike-sezer-9NRRCTGKYS4-unsplash.jpg?ref=rss" width="191" height="300"> <img src="https://leanrada.com/notes/css-only-lqip/gallery/daniel-b-herrmann-squbLwpQRQ8-unsplash.jpg?ref=rss" width="240" height="300"> <a href="https://leanrada.com/notes/css-only-lqip/javascript:(()=%3E%7Blet%20s;(s=document.getElementById('lqip-debug'))?s.remove%28%29%3A%28%28s=document.createElement%28%27style%27%29%29.innerHTML%3D%60%5Bstyle*%3D%27--my-lqip%3A%27%5D%3Anot%28%3Ahover%29%7Bobject-position%3Acalc%28infinity*1px%29%21important%7D%60%2Cs.id%3D%27lqip-debug%27%2Cdocument.head.append%28s%29%29%7D%29%28%29&amp;ref=rss"> Toggle images </a> </lqip-examples> <h2>Appendix: Alternatives considered</h2> <h3>Four colours instead of monochromatic preview</h3> <p>Four 5-bit colours, where each <b>R</b> is 2 bits, <b>G</b> is 2 bits, and <b>B</b> is just a zero or one.</p> <p>The four colours would map to the four corners of the image box, rendered as radial gradients</p> <p>This was my first attempt, and I fiddled with this for a while, but mixing four colours properly require proper bilinear interpolation and probably a shader. Just layering gradients resulted in muddiness (just like mixing too many watercolour pigments), and there was no CSS blend mode that could fix it. So I abandoned it, and moved on to a monochromatic approach.</p> <h3>Single solid colour</h3> <p>This was what I used on this website before. It’s simple and effective. A clean-markup approach could still use the custom <code>--lqip</code> variable:</p> <pre><code>&lt;img src="…" style="--lqip:#9bc28e"&gt; &lt;style&gt; /* we save some bytes by ‘aliasing’ this property */ * { background-color: var(--lqip) } &lt;/style&gt;</code></pre> <h3>HTML attribute instead of CSS custom property</h3> <p>We can use HTML attributes to control CSS soon! Here’s what the LQIP markup would look like in the future:</p> <pre><code>&lt;img src="…" lqip="192900"&gt;</code></pre> <p>Waiting for <a href="https://developer.chrome.com/blog/advanced-attr" target="_blank"><code>attr()</code> Level 5</a> for this one. It’s nicer and shorter, fewer weird punctuations in markup <small>(who came up with the double dash for CSS vars anyway?)</small>. The value can then be referenced in CSS with <code>attr(lqip type(&lt;number&gt;))</code> instead of <code>var(--lqip)</code>.</p> <p>For extra safety, a <code>data-</code> prefix could be added to the attribute name.</p> <p>Can’t wait for this to get widespread adoption. I also want it for my <a href="https://jordanbrennan.hashnode.dev/tac-a-new-css-methodology" target="_blank">TAC components</a>.</p>

2025/3/30
阅读更多

Inline rendering with document​.currentScript

<p>For quick and dirty rendering of simple dynamic content, you may not need the complexity of a templating language like Handlebars or a PHP backend.</p> <p>Let’s use the example phrase, <i>“Come on, it’s <code>&lt;currentYear&gt;</code>”</i>. It should result in <i>“Come on, it’s <u><noscript>2025</noscript><script>$(new Date().getFullYear())</script></u>”</i> when rendered today.</p> <p>You can write this directly in HTML—<em>without IDs, classes, or querySelectors in your JS!</em> Thanks to the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/currentScript" target="_blank"><code>document.currentScript</code></a> property, we can refer to the <em>currently running <code>&lt;script&gt;</code> element</em> directly and go from there.</p> <p>So the dynamic phrase <strong>“Come on, it’s <noscript>2025</noscript><script>$(new Date().getFullYear())</script>”</strong> would now be written as:</p> <pre><code>Come on, it’s &lt;script&gt; document.currentScript.replaceWith(new Date().getFullYear()) &lt;/script&gt;</code></pre> <p>The script simply replaces itself with its computed value on the spot.</p> <p>The code’s a bit wordy though, but we can <em>alias</em> it to a constant like <code>$</code> via <code>$=(...n)=&gt;document.currentScript.replaceWith(...n)</code>. Then we’d have something reminiscent of <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals" target="_blank">template literals</a> in JS.</p> <pre><code>Come on, it’s &lt;script&gt;$(new Date().getFullYear())&lt;/script&gt;</code></pre> <p>The code is pretty readable at a glance without context (after you get past the bit of indirection that is the <code>$</code> alias).</p> <box-note role="complementary"> <details> <summary>Click to see the WebComponent version! <small>Disclaimer: This is a joke.</small></summary> <code-block language="js"> <code><pre>&lt;script&gt;$(RenderJS.toString())&lt;/script&gt;</pre></code> </code-block> <p>This is the <code>RenderJS</code> custom element. All it does is replace itself with the result of its contents treated as JavaScript code.</p> <p>To use it, we must first name the tag. The beloved dollar sign <code>$</code> is not allowed, so we use the next best thing—the emoji 💲. Sadly, that’s not enough: Tag names must start with a letter of the alphabet and include a hyphen. As such, we are forced to name it: <code>j-💲</code> representing JavaScript.</p> <code-block language="js"> <code><pre>&lt;script&gt;customElements.define('j-💲', RenderJS);&lt;/script&gt;</pre></code> </code-block> <h3>Example usage</h3> <fixed-grid> <code-block> <code><pre>I overslept by about &lt;j-💲&gt;Math.PI&lt;/j-💲&gt; hours this morning.</pre></code> </code-block> <card-box> I overslept by about <j-💲>Math.PI</j-💲> hours this morning. </card-box> </fixed-grid> </details> </box-note> <p>A <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript" target="_blank"><code>noscript</code></a> fallback would be nice for those that don’t run JS.</p> <pre><code>Come on, it’s &lt;noscript&gt;current year&lt;/noscript&gt; &lt;script&gt;$(new Date().getFullYear())&lt;/script&gt;</code></pre> <p>The resulting script-noscript juxtaposition in the markup looks almost like Angular/Vue’s if-else markup, neat.</p> <box-note>IIRC, I learned about this technique from the <a href="https://github.com/gnat/surreal" target="_blank">Surreal</a> library or something similar. But I’m sure this is not a new discovery. These APIs have been standardised for a long time.</box-note> <h2>Real world examples</h2> <p>If you thought this technique is only useful for rendering the current year, you’re mostly right! As an example, I’ve used inline scripts to refer to relative dates in my living pages. Because <strong>“<noscript>8</noscript><script>$(new Date().getFullYear()-2017)</script> years ago”</strong> sounds more natural than <strong>“2017”</strong>.</p> <pre><code>It was planted &lt;noscript&gt;in 2017&lt;/noscript&gt; &lt;script&gt;$(new Date().getFullYear()-2017, ' years ago')&lt;/script&gt; but now it has grown tall and strong.</code></pre> <card-box> It was planted <noscript>in 2017</noscript> <script>$(new Date().getFullYear()-2017, ' years ago')</script> but now it has grown tall and strong. </card-box> <h3>Random greeting</h3> <p>Another example is in my front page, which says a randomly selected greeting on each visit.</p> <img src="https://leanrada.com/notes/inline-rendering-currentscript/greeting.png?ref=rss" alt="screenshot of my homepage intro section"> <pre><code>&lt;span class="intro-line"&gt; &lt;noscript&gt;Hello!&lt;/noscript&gt; &lt;script&gt; $([ "Hello!", "Hey~~~", "What’s up ↑", "Hi there →", "Hey there →", ][Date.now() % 5]) &lt;/script&gt; &lt;/span&gt;</code></pre> <box-note>The randomiser is just a <code>Date.now()</code> with modulo because it was more concise than <code>Math.floor(Math.random() * 5)</code>.</box-note> <h3>Splitting text for animation</h3> <p>Split text into characters using inline scripts to generate markup per character which can then be controlled or animated! In my case, I used it to render circular text, but a more common use case is text animation like the following.</p> <card-box> It has been a <script>document.currentScript.outerHTML= "supercalifragilisticexpialidocious".replace(/./g, c => `<strong>${c}</strong>`) </script> week. </card-box> <pre><code>It has been a &lt;script&gt;document.currentScript.outerHTML= "supercalifragilisticexpialidocious" .replace(/./g, c =&gt; `&lt;strong&gt;${c}&lt;/strong&gt;`) &lt;/script&gt; week. &lt;style&gt; strong { display: inline-block; animation: wave 0.3s infinite alternate ease-in-out; &amp;:nth-child(4n + 1) { animation-delay: -0.6s; } &amp;:nth-child(4n + 2) { animation-delay: -0.45s; } &amp;:nth-child(4n + 3) { animation-delay: -0.3s; } &amp;:nth-child(4n + 4) { animation-delay: -0.15s; } } @keyframes wave { to { transform: translateY(-2px); } } &lt;/style&gt;</code></pre> <h3>Rendering metadata to reduce duplication</h3> <p>This post’s heading is dynamically derived from the page title metadata. I only have to define this post’s title once, in the <code>&lt;title&gt;</code> tag.</p> <code><pre>&lt;blog-header&gt; &lt;h1&gt;&lt;script&gt;$(document.title)&lt;/script&gt;&lt;/h1&gt; &lt;img src="hero.jpg" alt="" loading="lazy"&gt;&lt;/img&gt; &lt;/blog-header&gt;</pre></code> <h3>Auto-updating footer</h3> <p>How about an auto-updating copyright notice in the footer which may or may not have legal implications? <small>(IANAL)</small></p> <pre><code>&lt;footer&gt; © &lt;script&gt;$(new Date().getFullYear())&lt;/script&gt; Mycorp, Ltd. &lt;/footer&gt;</code></pre> <p>For comparison, here’s the StackOverflow/ChatGPT answer:</p> <fixed-grid> <code-block language="js"> <pre><code>document .getElementById("copyright-year") .textContent = new Date().getFullYear();</code></pre> </code-block> <code-block language="html"> <pre><code>&lt;footer&gt; © &lt;span id="copyright-year"&gt;&lt;/span&gt; Mycorp, Ltd. &lt;/footer&gt;</code></pre> </code-block> </fixed-grid> <p>Why pollute the global ID namespace and separate coupled code if we can avoid it?</p> <box-note><code>Date().split` `[3]</code> is also a short (but very hacky) way to get the year.</box-note> <p>At this point you may be asking, is this technique only really useful for rendering the current year? Maybe, but we can do more than just <em>rendering</em> text.</p> <p>Come on, it’s <noscript>2025</noscript><script>$(new Date().toLocaleDateString())</script>, the web is rich and interactive!</p> <h3>The ubiquitous counter app example</h3> <p>This is not a simple templating example anymore, but shows the power of <code>currentScript</code> in <a href="https://en.wikipedia.org/wiki/Hydration_(web_development)" target="_blank">hydrating</a> self-contained bits of interactive HTML.</p> <card-box> Count: <span>0</span> <button theme="button">Increment</button> <button theme="button">Decrement</button> <script> const [span, increment, decrement] = document.currentScript.parentElement.children; let count = 0; increment.onclick = () => span.replaceChildren(++count); decrement.onclick = () => span.replaceChildren(--count); </script> </card-box> <pre><code>&lt;div&gt; Count: &lt;span&gt;0&lt;/span&gt; &lt;button&gt;Increment&lt;/button&gt; &lt;button&gt;Decrement&lt;/button&gt; &lt;script&gt; const [span, increment, decrement] = document.currentScript.parentElement.children; let count = 0; increment.onclick = () =&gt; span.replaceChildren(++count); decrement.onclick = () =&gt; span.replaceChildren(--count); &lt;/script&gt; &lt;/div&gt;</code></pre> <p>I used this ‘local script’ pattern all the time in a previous version of this blog. It’s useful when making interactive illustrations in the middle of a long post. You wouldn’t want to put that logic at the very end or start of the file, away from the relevant section! Same goes for styles, now made even better with the <a href="https://developer.chrome.com/docs/css-ui/at-scope#prelude-less_scope" target="_blank"><code>@scope</code> rule</a>.</p> <p>It’s a good way to manage islands of interactivity.</p> <h2>The catch</h2> <p>There are reasons people gravitate to libraries and frameworks. They’re just too convenient.</p> <p>If you want to use the alias definition above—the <code>$</code> shorthand—then you'd first have to define it at the top of the HTML, <strong>synchronously</strong>. Say you want to use this in multiple pages, so you put this in a common script file and load it via <code>&lt;script src="$.js"&lt;/script&gt;</code>. This could result in a parser- and render-blocking fetch: potentially very bad for performance!</p> <p>For my own cases, I don’t use the alias. I straight up just use <code>document.currentScript​.replaceWith</code>. Wish it was <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/write" target="_blank">shorter…</a></p> <p>Also, you cannot do <strong>data-driven templating</strong> this way, in the sense that you have <strong>templates</strong> to apply <strong>data</strong> onto. Metadata such as date published, post title, and tags that are stored in some posts table or in frontmatter somewhere, cannot be used for inline scripting purposes. You can’t fetch data from inline scripts. Therefore, unless data is somehow pre-injected in the global scope, no luck in pure vanilla JS templating. This is where templating languages like <a href="https://shopify.github.io/liquid/" target="_blank">Liquid</a> really shine.</p> <p>For my site, I don’t need a templating system—I use direct patching to data-drive my HTML (in which the source and the output files are the same).</p> <p>Even if you use a templating framework or a static site generator, inline scripts remain useful!</p> <h2>Further reading</h2> <ul> <li><a href="https://github.com/gnat/surreal" target="_blank">Surreal</a> - a library that offers this technique, plus other jQuery-style helpers. Note the synchronous script tag loading issue though!</li> <li><a href="https://github.com/sveltejs/kit/issues/2221" target="_blank">currentScript discussion in SvelteKit</a> - this as an alternative to global IDs for self-hydrating things. In the end they went with generated IDs. :|</li> </ul> <h2>Appendix</h2> <p><a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/write" target="_blank"><code>document.write()</code></a> used to (?) work like this for inline rendering, but it’s now deprecated because it had inconsistent behaviour.</p> <code><pre>Come on, it’s &lt;script&gt;document.write(new Date().getFullYear())&lt;/script&gt;, stop using &lt;code&gt;document.write&lt;/code&gt;!</pre></code><iframe srcdoc="Come on, it’s <noscript>2025</noscript><script>document.write(new Date().getFullYear())</script>, stop using <code>document.write</code>!"></iframe>

2025/3/7
阅读更多

Rewriting my site in vanilla web

<p>I rewrote this website in vanilla HTML/CSS/JS. Here’s the story.</p> <h2>But why?</h2> <p>Over the years, I’ve used a bunch of libraries &amp; frameworks to build <a href="https://leanrada.com/wares/personal-website?ref=rss">this website</a>, before finally making my own static site generator that I called <a href="https://github.com/Kalabasa/compose-html" target="_blank"><code>compose-html</code></a>. As the name suggests, it composes HTML files together, very much like <a href="https://astro.build/" target="_blank">Astro</a>.</p> <figure> <img src="https://leanrada.com/notes/vanilla-web-rewrite/compose-html.png?ref=rss" alt="screenshot of compose-html README"> <figcaption>compose-html’s README</figcaption> </figure> <p>I like systems that have a small set of tight abstractions that are easy to understand. I’m not a fan of rigid, non-atomic concepts like “pages” and “layouts”, “themes”, “frontmatters” — I mean, these are just ‘components’ and ‘data’! I dislike those that dictate your project directory structure and coding style.</p> <figure> <img src="https://leanrada.com/notes/vanilla-web-rewrite/project-structure-docs.png?ref=rss" alt="screenshot of a framework’s documentation"> <figcaption>If your documentation has a ‘Project structure’ section, I’m out!</figcaption> </figure> <p>So I built my own simple site builder and that was nice BUT it didn’t end up making life easier. The real world is messy, and HTML more so. Simply composing pieces of HTML together isn’t that straightforward and the abstraction leaked. My <code>compose-html</code> framework eventually turned into a 2k LoC that was more liability than freedom. Though it served me very well, it was a dead end.</p> <h2>Maybe nothing can solve my problem…</h2> <p>As in, literally nothing. No framework. No build steps.</p> <p>What if HTML wasn’t a render target, but was both the authoring and publishing medium?</p> <p>What if I rewrote my site in vanilla HTML/CSS/JS? A crazy idea infiltrated my conciousness.</p> <h2>Is it feasible?</h2> <p>A common reason for adding complexity is to avoid repetitive work like copying headers &amp; footers to every page. So we have PHP, Handlebars, Next.JS.</p> <p>Modern HTML/JS now has <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components" target="_blank"><strong>Web Components</strong></a>, custom elements which could be used to encapsulate repetitive sections! <small>This was already possible without Web Components, but it makes it nicer.</small></p> <p>One could go write HTML like this:</p> <pre><code>&lt;!doctype html&gt; &lt;site-header&gt;&lt;/site-header&gt; &lt;main&gt; My page's content &lt;/main&gt; &lt;site-footer&gt;&lt;/site-footer&gt;</code></pre> <box-note><strong>What about the repetitive &lt;html&gt;, &lt;head&gt;, and &lt;body&gt; tags?</strong> Fortunately, web browsers and the HTML spec itself are lenient. These tags are <a href="https://html.spec.whatwg.org/#optional-tags" target="_blank">actually optional</a>!</box-note> <p>One would still need to manually copy and paste some common tags like the <code>&lt;script&gt;</code> to load the custom elements, and maybe a <code>common.css</code> file and a few meta tags. But I’d say it’s a similar level of boilerplate as some other frameworks, if not a bit un-DRY.</p> <box-note><strong>What about people who disable JS?</strong> No problem. They would still see the main content itself, just not the navigational headers &amp; footers. I presume these people would be savvy enough to navigate by URL.</box-note> <p>Another reason to use a generator is to generate data-driven content, especially for blog sites which usually have a blog index page with autogenerated collections of posts.</p> <figure> <img src="https://leanrada.com/notes/vanilla-web-rewrite/blog-list.png?ref=rss" alt="screenshot of a blog post list"> <figcaption>A chronological list of posts.</figcaption> </figure> <p>I don’t want to hand-code lists of posts. Especially since a slice of the latest posts is mirrored in the homepage. As I said, the real world is messy, and there is not one absolute dogma that can solve it all. A bit of automation is perfectly fine whenever needed! Just there’s no need to build-systemify the entire site.</p> <p>With these concerns out of the way, the rewrite was looking more feasible.</p> <h2>My approach</h2> <p>To make sense of the rewrite and keep the site maintainable going forward, I decided to follow these principles:</p> <ol> <li>Semantic HTML</li> <li>TAC CSS methodology</li> <li> Web Components with Light DOM </li> </ol> <h3>1. Semantic HTML</h3> <p>Basically means using semantic tags instead of generic <code>div</code>s and <code>span</code>s</p> <p>One example is the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time" target="_blank"><code>time</code></a> tag that I used to indicate post date.</p> <pre><code>&lt;time datetime="2025-02-26"&gt;26 Feb 2025&lt;/time&gt;</code></pre> <p>Along the usual benefits of semantic HTML, the variety of tags will come in handy in this very rewrite, which will become obvious in the next point.</p> <h3>2. TAC methodology</h3> <p><a href="https://jordanbrennan.hashnode.dev/tac-a-new-css-methodology" target="_blank">TAC methodology</a> is a modern CSS approach takes advantage of the modern web.</p> <p>The main takeaway is that we should make up <strong>new tags</strong> instead of divs-with-classes to represent conceptual components. For example:</p> <pre><code>&lt;blog-post-info hidden&gt; &lt;time datetime="2025-02-26"&gt;26 Feb 2025&lt;/time&gt; · 1 min read &lt;/blog-post-info&gt;</code></pre> Contrast that with, let’s say, <a href="https://getbem.com/" target="_blank">BEM methodology</a>: <pre><code>&lt;div class="blog-post-info blog-post-info_hidden"&gt; &lt;time class="blog-post-info__date" datetime="2025-02-26"&gt; 26 Feb 2025 &lt;/time&gt; · 1 min read &lt;/div&gt;</code></pre> By making up a new tag called <code>blog-post-info</code>, the styling of these elements could easily use <strong>tag</strong> and <strong>attribute</strong> selectors (the T and A of TAC!) without the need for classes! The markup is leaner, and the CSS even looks modular when taking advantage of modern CSS nesting: <pre><code>blog-post-info { display: block; /* note: made-up tags default to `inline` */ color: #fff; &amp;[hidden] { display: none; } /* semantic HTML helps narrow the element to select */ &gt; time { color: #ccc; font-weight: bold } }</code></pre> <p>While TAC was called a CSS methodology by the authors, it influences Web Component philosophy as well, into the next point.</p> <h3>3. Web Components with light DOM</h3> <p>I’ve always found the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components" target="_blank">Web Component</a> abstraction to be a bit heavy. You have the Shadow DOM, encapsulation modes (?), slots, templates, and many more related concepts. Now, some of those are pretty useful like slots and templates (which aren’t exclusive to Web Components). But overall, Web Components feel a bit clunky.</p> <p>The <a href="https://meyerweb.com/eric/thoughts/2023/11/01/blinded-by-the-light-dom/" target="_blank">‘light DOM’</a> approach does away with all of that. Like the example above:</p> <pre><code>&lt;blog-post-info hidden&gt; &lt;time datetime="2025-02-26"&gt;26 Feb 2025&lt;/time&gt; · 1 min read &lt;/blog-post-info&gt;</code></pre> <p>If implemented with shadow DOM, it could’ve look like this:</p> <pre><code>&lt;blog-post-info datetime="2025-02-26" minread="1"&gt;&lt;/blog-post-info&gt; &lt;!-- or maybe --&gt; &lt;blog-post-info datetime="2025-02-26"&gt; 1 min read &lt;/blog-post-info&gt;</code></pre> <p>The light DOM aligns with the TAC methodology, so it’s a good match.</p> <p>I admit scoped styles and slots are neat, but there aren’t essential (see TAC) and there are workarounds to slots. <strong>I’m not making a modular design system after all.</strong></p> <p>Using the light DOM also provides a smoother transition from plain JS style to Web Components. Relevant, as I was converting some old JS code. Imagine the following common pattern:</p> <pre><code>for (const blogPostInfo of document.querySelectorAll(".blog-post-info")) { const time = blogPostInfo.querySelector("time"); // ... initialisation code }</code></pre> This pattern maps neatly to Web Component style: <pre><code>customElements.define( "blog-post-info", class BlogPostInfo extends HTMLElement { connectedCallback() { const time = this.querySelector("time"); // ... initialisation code } } );</code></pre> <p>The mapping was straightforward enough that I was able to partially automate the conversion via <a href="https://en.wikipedia.org/wiki/GitHub_Copilot" target="_blank">LLM</a>.</p> <p>While I’m not really making the most out of Web Components technology, I don’t actually need the extra features. I have a confession — I set <code>this.innerHTML</code> directly within a Web Component, and it’s so much simpler than setting up <code>template</code>s. I do try to sanitize.</p> <p>Details aside, these principles made the whole rewrite easier because it reduced the amount of actual refactoring. I wasn’t able to particularly follow them to the letter, especially for nasty old code. But for future code, I hope to keep using these techniques.</p> <h2>A brief premature retrospective</h2> <p><strong>Pros:</strong></p> <ul> <li>Instant feedback loop. Zero build time.</li> <li>No bugs out of my control. <small>It’s what turned me off <a href="https://www.11ty.dev/" target="_blank">Eleventy</a>.</small></li> <li>No limitations imposed by framework or paradigm.</li> </ul> <p><strong>Cons:</strong></p> <ul> <li>Big common files, <code>common.js</code> and <code>common.css</code>, ‘cause no bundler.</li> <li>Verbose. No shortcuts, e.g. anchor tag — compare markdown links.</li> <li>Frequent copy pasting.</li> <li>Harder to redesign the site now.</li> </ul> <p>I’m fine with a little bit of verbosity. For contrast, I wrote the <a href="https://leanrada.com/htmz/" target="_blank">htmz page</a> manually in plain HTML, <em>including the syntax-highlighted code snippets!</em></p> <figure> <img src="https://leanrada.com/notes/vanilla-web-rewrite/htmz-page-code.png?ref=rss" alt="source code of htmz page"> <figcaption>Have you ever tried manual syntax highlighting?</figcaption> </figure> <p>But not this time, I added the <a href="https://prismjs.com/" target="_blank">Prism.js</a> library to automate syntax highlighting.</p> <h2>Tips &amp; tricks</h2> <p><strong>AI —</strong> I used LLMs to help me convert a bunch of pages into the new style. What I did was give it an example of an old page and the converted version (manually converted by me), and then it gave me a converter script that I tweaked and ran through most of the pages. I did the same to convert components and it was a breeze. The converted script was iteratively improved upon and made more robust by me and the LLM via examples of incorrect conversions and corrected versions. I guess the trick was to give it more examples instead of more elaborate instructions.</p> <pre><code>// this snippet from the AI-assisted converter script // converts &lt;blog-post-info&gt; elements input("blog-post-info").each((i, el) =&gt; { const tag = input(el); const hidden = tag.attr("hidden") != null; const date = tag.attr("date"); const readMins = tag.attr("read-mins"); let out = `&lt;blog-post-info${hidden ? " hidden" : ""}&gt;\n`; const dateDate = new Date(date); const yyyy = dateDate.getFullYear(); const mm = (dateDate.getMonth() + 1).toString().padStart(2, "0"); const dd = dateDate.getDate().toString().padStart(2, "0"); out += ` &lt;time datetime="${yyyy}-${mm}-${dd}"&gt;${date}&lt;/time&gt;\n`; out += ` · ${readMins} min read\n`; out += `&lt;/blog-post-info&gt;`; tag.remove(); main.before(out + "\n\n"); });</code></pre> <p><strong>Autoload —</strong> I added client-side JS that searched for custom tags and loaded the appropriate script files when those tags enter the viewport. In short, <em>lazy loading components</em>. I did have to impose a rigid file structure, because whenever it encounters a tag it would try to <code>import(`/components/${tagName}.js`)</code> — all my autoloaded components had to be in that flat directory. Am I a hypocrite? No, I can change that rule anytime.</p> <pre><code>// autoloads components in the viewport new IntersectionObserver((entries) =&gt; { for (const entry of entries) { if (entry.isIntersecting) { if (components.has(entry.target.tagName)) { import(`/components/${entry.target.tagName}.js`); components.delete(entry.target.tagName); } intersectionObserver.unobserve(entry.target); } } });</code></pre> <p><strong>This is not an exercise in purity —</strong> This is a real website, a personal one at that. This is not a pure HTML proof-of-concept. Not a TAC role model. Not a Web Component masterpiece. I would add inline JS whenever it’s more convenient, break encapsulation if necessary, use classes instead of pure tag selectors. Don’t let the ideal pure plain static TAC+WebComponent vanilla HTML+CSS get in the way of finishing the project. In other words, pragmatism over principles.</p> <h2>Homepage redesign</h2> <p>I couldn’t resist the temptation to not just port, but redesign the site (at least, the homepage).</p> <prose-bleed> <img src="https://leanrada.com/notes/vanilla-web-rewrite/homepage-sections.png?ref=rss" alt="screenshot of sections"> </prose-bleed> <p>The homepage sections are now more dense, more desktop-equal (not mobile-first), and the bento section has been revamped!</p> <figure> <prose-bleed> <img src="https://leanrada.com/notes/vanilla-web-rewrite/homepage-bento.png?ref=rss" alt="screenshot of bento section"> </prose-bleed> <figcaption>See also, autoupdating note count, project count, GitHub stats, and hit counter. Sprinkles of automation, no build system required!</figcaption> </figure> <p>I’ll probably add a live Spotify card in there somewhere.</p> <hr><prose-bleed> <img src="https://leanrada.com/notes/vanilla-web-rewrite/diff.png?ref=rss" alt="1536 changed files, 25511 additions, 19076 deletions."> </prose-bleed> <p>That’s about it! The whole site rewrite went smoother and quicker than expected! And I’m quite liking the raw authoring experience. Now, how long will this new paradigm hold up? 😏</p> <img src="https://leanrada.com/notes/vanilla-web-rewrite/notebook.jpg?ref=rss" alt="notebook">

2025/3/1
阅读更多

CSS sprite sheet animations

<p><em>For RSS readers: This article contains interactive content available on the <a href="https://leanrada.com/notes/css-sprite-sheets?ref=rss">original post on leanrada.com</a>.</em></p> <p>Check out this demo first (Click it!):</p> <label> <input type="checkbox"> <div> <div role="img" alt="interactive heart animation"> </div> </div> Heart </label> <p>Yes, it’s the Twitter heart button. This heart animation was done using an old technique called <a target="_blank" href="https://en.wikipedia.org/wiki/Texture_atlas"><strong>sprite sheets</strong><span>🡵</span></a>.</p> <pre>Interactive content: <a href="https://leanrada.com/notes/css-sprite-sheets?ref=rss">Visit the website to play with interactive content!</a> Alternative text: spinning clover</pre> <pre>Interactive content: <a href="https://leanrada.com/notes/css-sprite-sheets?ref=rss">Visit the website to play with interactive content!</a> Alternative text: spinning clover</pre> <pre>Interactive content: <a href="https://leanrada.com/notes/css-sprite-sheets?ref=rss">Visit the website to play with interactive content!</a> Alternative text: spinning clover</pre> <p>On the web sprite sheets are used mainly to reduce the amount of HTTP requests by bundling multiple images together into a single image file. Displaying a sub-image involves clipping the sheet in the appropriate coordinates.</p> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="100%" data-placeholder="" src="https://leanrada.com/notes/css-sprite-sheets/minecraft.png?ref=rss" caption="Sprite sheet / texture atlas of Minecraft blocks"> <span>Sprite sheet / texture atlas of Minecraft blocks</span> <p>The bandwidth benefit has been largely mitigated by HTTP/2 now, but sprite sheets have another purpose: <strong>animations!</strong> Displaying animations is one of the primary uses of sprite sheets, besides loading performance.</p> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="100%" data-placeholder="" src="https://leanrada.com/notes/css-sprite-sheets/animation-opaque.png?ref=rss" caption="Characters w/ animations, sprite sheet by <text-link href='https://opengameart.org/content/classic-hero-and-baddies-pack'>GrafxKid</text-link>"> <span>Characters w/ animations, sprite sheet by <a target="_blank" href="https://opengameart.org/content/classic-hero-and-baddies-pack">GrafxKid</a></span> <p>It’s neat for small raster-based animations such as loading spinners, characters, icons, and micro-interactions.</p> <pre>Interactive content: <a href="https://leanrada.com/notes/css-sprite-sheets?ref=rss">Visit the website to play with interactive content!</a> Alternative text: walking animation of a red monster character</pre> <pre>Interactive content: <a href="https://leanrada.com/notes/css-sprite-sheets?ref=rss">Visit the website to play with interactive content!</a> Alternative text: walking animation of a blue monster character</pre> <h2 id="how">How</h2> <p>Assumming you already have a sprite sheet image and coordinates in hand, all you need is a way to clip that image for display. There are a few ways to clip an image in CSS.</p> <table> <thead> <tr> <th>method</th> <th>coordinates via</th> </tr> </thead> <tbody> <tr> <td><code>background-image</code></td> <td><code>background-position</code></td> </tr> <tr> <td><code>overflow: hidden</code> with nested <code>&lt;img&gt;</code></td> <td><code>left</code>, <code>top</code> on the nested element</td> </tr> <tr> <td><code>clip-path</code></td> <td><code>clip-path</code>, <code>left</code>, <code>top</code></td> </tr> </tbody> </table> <p>The <code>left</code> and <code>top</code> rules can be substituted for <code>transform: translate(…)</code>.</p> <p>The <code>background-image</code> way is the most convenient since you only need one element.</p> <pre><code><span>.element</span> <span>{</span> <span>background-image</span><span>:</span> <span><span>url</span><span>(</span><span>'heart.png'</span><span>)</span></span><span>;</span> <span>/* size of one frame */</span> <span>width</span><span>:</span> 100px<span>;</span> <span>height</span><span>:</span> 100px<span>;</span> <span>/* size of the whole sheet */</span> <span>background-size</span><span>:</span> 2900px 100px<span>;</span> <span>/* coordinates of the desired frame (negated) */</span> <span>background-position</span><span>:</span> -500px 0px<span>;</span> <span>}</span></code></pre> <p>This is the sprite sheet for the heart animation from Twitter:</p> <img srcset="" sizes="" spec="100% [800) 70% [1750) 1225" loading="lazy" width="400%" src="https://leanrada.com/notes/css-sprite-sheets/twitter.png?ref=rss" type="bleed" alt="Heart animation sprite sheet from Twitter"> <p>Using this image, the code above produces a still image of the frame at (500,0) — the sixth frame.</p> <figure role="img" alt="6th frame of the heart animation"></figure> <p>Removing the clipping method reveals that it’s just a part of the whole sheet (this view will be fun when it’s actually animating):</p> <figure role="img" alt="6th frame of the heart animation"> <div> </div> </figure> <p>If the sprite sheet wasn’t made to be animated, that is, if it was just a collection of multiple unrelated sub-images like the Minecraft example earlier, then the CSS rules above are all we need to know. That’s it.</p> <p>Since this sprite sheet was made to be animated, that is, it contains animation frames, more needs to be done.</p> <p>To animate this, we animate the <code>background-position</code> over each frame in the sequence, flashing each frame in quick succession.</p> <pre><code><span><span> </span><span>.element</span> <span>{</span> <span> </span> <span>background-image</span><span>:</span> <span><span>url</span><span>(</span><span>'heart.png'</span><span>)</span></span><span>;</span> <span> </span> <span>/* size of one frame */</span> <span> </span> <span>width</span><span>:</span> 100px<span>;</span> <span> </span> <span>height</span><span>:</span> 100px<span>;</span> <span> </span> <span>/* size of the whole sheet */</span> <span> </span> <span>background-size</span><span>:</span> 2900px 100px<span>;</span> </span><span><span>-</span> <span>/* coordinates of the desired frame (negated) */</span> <span>-</span> <span>background-position</span><span>:</span> -500px 0px<span>;</span> </span><span><span>+</span> <span>/* animate the coordinates */</span> <span>+</span> <span>animation</span><span>:</span> heartAnimation 2s <span>steps</span><span>(</span>29<span>,</span> jump-none<span>)</span> infinite<span>;</span> <span>+</span><span>}</span> <span>+</span> <span>+</span><span><span>@keyframes</span> heartAnimation</span> <span>{</span> <span>+</span> <span>from</span> <span>{</span> <span>+</span> <span>/* first frame */</span> <span>+</span> <span>background-position</span><span>:</span> 0px 0px<span>;</span> <span>+</span> <span>}</span> <span>+</span> <span>to</span> <span>{</span> <span>+</span> <span>/* last frame */</span> <span>+</span> <span>background-position</span><span>:</span> -2800px 0px<span>;</span> <span>+</span> <span>}</span> <span>+</span><span>}</span></span></code></pre> <p><span><strong>Important: Note the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function#stepsinteger_step-position"><code>steps()</code><span>🡵</span></a> timing function in the <code>animation</code> rule above!</strong> This is required for the transition to land exactly <em>on</em> the frames.</span></p> <p>Voilà.</p> <figure role="img" alt="the heart animation"></figure> <p>And the view without clipping:</p> <figure role="img" alt="the heart animation (unclipped)"> <div> </div> </figure> <img loading="lazy" width="100%" data-placeholder="" src="https://leanrada.com/notes/css-sprite-sheets/zoetrope.gif?ref=rss" caption="It’s like a <text-link href='https://deniseanimation.weebly.com/zoetropes.html'>zoetrope</text-link>"> <span>It’s like a <a target="_blank" href="https://deniseanimation.weebly.com/zoetropes.html">zoetrope</a></span> <p>The exact parameters for the <code>steps()</code> function are a bit fiddly and it depends on whether you loop it or reverse it, but here’s what worked for the heart animation with 29 total frames.</p> <pre><code><span>animation-timing-function</span><span>:</span> <span>steps</span><span>(</span>29<span>,</span> jump-none<span>)</span><span>;</span></code></pre> <p>Using any other timing function results in a weird smooth in-betweening movement like this:</p> <figure role="img" alt="janky heart animation"></figure> <p>Remember, <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function#stepsinteger_step-position"><code>steps()</code><span>🡵</span></a> is crucial!</p> <h2 id="why-not-apng">Why not APNG?</h2> <p>For autoplaying stuff like loading spinners, you might want plain old GIFs or <a target="_blank" href="https://en.wikipedia.org/wiki/APNG">APNGs<span>🡵</span></a> instead.</p> <p>But we don’t have tight control over the playback with these formats.</p> <p>With sprite sheets, we can pause, reverse, play on hover, change the frame rate…</p> <p>…make it <strong>scroll-driven</strong>,</p> <figure> <div> <pre>Interactive content: <a href="https://leanrada.com/notes/css-sprite-sheets?ref=rss">Visit the website to play with interactive content!</a> Alternative text: spinning globe</pre> <pre>Interactive content: <a href="https://leanrada.com/notes/css-sprite-sheets?ref=rss">Visit the website to play with interactive content!</a> Alternative text: spinning moon</pre> <pre>Interactive content: <a href="https://leanrada.com/notes/css-sprite-sheets?ref=rss">Visit the website to play with interactive content!</a> Alternative text: spinning globe</pre> </div> <figcaption>Note: Scroll-driven animations are experimental. No Firefox support atm.</figcaption> </figure> <p>… or make it <strong>interactive</strong>!</p> <h2 id="interactivity">Interactivity</h2> <p>The nice thing about this being in CSS is that we can make it interactive via selectors.</p> <p>Continuing with the heart example, we can turn it into a stylised toggle control via HTML &amp; CSS:</p> <label> <input type="checkbox"> <div> <div role="img" alt="interactive heart animation"> </div> </div> Heart </label> <pre><code><span><span> </span><span>.element</span> <span>{</span> <span> </span> <span>background-image</span><span>:</span> <span><span>url</span><span>(</span><span>'heart.png'</span><span>)</span></span><span>;</span> <span> </span> <span>/* size of one frame */</span> <span> </span> <span>width</span><span>:</span> 100px<span>;</span> <span> </span> <span>height</span><span>:</span> 100px<span>;</span> <span> </span> <span>/* size of the whole sheet */</span> <span> </span> <span>background-size</span><span>:</span> 2900px 100px<span>;</span> </span><span><span>+</span> <span>}</span> <span>+</span> <span>+</span><span>.input:checked ~ .element</span> <span>{</span> </span><span><span> </span> <span>/* animate the coordinates */</span> </span><span><span>-</span> <span>animation</span><span>:</span> heartAnimation 2s <span>steps</span><span>(</span>29<span>,</span> jump-none<span>)</span> infinite<span>;</span> </span><span><span>+</span> <span>animation</span><span>:</span> heartAnimation 2s <span>steps</span><span>(</span>29<span>,</span> jump-none<span>)</span> forwards<span>;</span> </span><span><span> </span><span>}</span> <span> </span> <span> </span><span><span>@keyframes</span> heartAnimation</span> <span>{</span> <span> </span> <span>from</span> <span>{</span> <span> </span> <span>/* first frame */</span> <span> </span> <span>background-position</span><span>:</span> 0px 0px<span>;</span> <span> </span> <span>}</span> <span> </span> <span>to</span> <span>{</span> <span> </span> <span>/* last frame */</span> <span> </span> <span>background-position</span><span>:</span> -2800px 0px<span>;</span> <span> </span> <span>}</span> <span> </span><span>}</span></span></code></pre> <p>Or use the new <code>:has(:checked)</code>.</p> <p><span>Additionally, CSS doesn’t block the main thread. In modern browsers, the big difference between CSS animations and JS-driven animations (i.e. <code>requestAnimationFrame</code> loops) is that the JS one runs on the main thread along with event handlers and DOM operations, so if you have some heavy JS (like React rerendering the DOM), JS animations would suffer along with it.</span></p> <p>Of course, JS could still be used, if only to <em>trigger</em> these CSS sprite animations by adding or removing CSS classes.</p> <h2 id="why-not-animated-svgs">Why not animated SVGs?</h2> <p>If you have a vector format, then an <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/SVG/SVG_animation_with_SMIL">animated SVG<span>🡵</span></a> is a decent option!</p> <p>This format is kinda hard to author and integrate though — one would need both animation skills and coding skills to implement it. Some paid tools apparently exist to make it easier?</p> <p>And Lottie? That 300-kilobyte library? Uh, sure, if you really need it.</p> <h2 id="limitations-of-sprite-sheets">Limitations of sprite sheets</h2> <ul> <li>The sheet could end up as a very large image file if you’re not very careful.</li> <li>It’s only effective for the narrow case of small frame-by-frame raster animations. Beyond that, better options may exist, such animated SVGs, the <code>&lt;video&gt;</code> tag, the <code>&lt;canvas&gt;</code> tag, etc.</li> <li>How do you support higher pixel densities? Media queries on <code>background-image</code>? <code>&lt;img&gt;</code> with <code>srcset</code> could work, but the coordinates are another matter. But it could be solved generally with CSS custom properties and <code>calc</code>.</li> </ul> <h2 id="gallery">Gallery</h2> <figure> <div> <pre>Interactive content: <a href="https://leanrada.com/notes/css-sprite-sheets?ref=rss">Visit the website to play with interactive content!</a> Alternative text: star animation from Twitter</pre> </div> <figcaption>‘Favorite’ icon animation</figcaption> </figure><figure> <div> <pre>Interactive content: <a href="https://leanrada.com/notes/css-sprite-sheets?ref=rss">Visit the website to play with interactive content!</a> Alternative text: walking animation of a snail</pre> <pre>Interactive content: <a href="https://leanrada.com/notes/css-sprite-sheets?ref=rss">Visit the website to play with interactive content!</a> Alternative text: idle hiding animation of a snail</pre> <pre>Interactive content: <a href="https://leanrada.com/notes/css-sprite-sheets?ref=rss">Visit the website to play with interactive content!</a> Alternative text: climb animation of a snail</pre> <pre>Interactive content: <a href="https://leanrada.com/notes/css-sprite-sheets?ref=rss">Visit the website to play with interactive content!</a> Alternative text: attack animation of a snail</pre> <pre>Interactive content: <a href="https://leanrada.com/notes/css-sprite-sheets?ref=rss">Visit the website to play with interactive content!</a> Alternative text: death animation of a snail</pre> </div> <figcaption>Snail enemy sprites from my game <a target="_blank" href="https://leanrada.com/wares/miniforts?ref=rss">MiniForts</a></figcaption> </figure> <video muted="" autoplay="" loop="" playsinline="" caption="I actually had to implement these hover animations via sprite sheets at work." data-src="/notes/css-sprite-sheets/work.mp4"> <source src="https://leanrada.com/notes/css-sprite-sheets/work.mp4?ref=rss"> Video: | Source: /notes/css-sprite-sheets/work.mp4 </video> <span>I actually had to implement these hover animations via sprite sheets at work.</span> <video muted="" autoplay="" loop="" playsinline="" media-class="u-media" caption="Behind the scenes" data-src="/notes/css-sprite-sheets/work2.mp4"> <source src="https://leanrada.com/notes/css-sprite-sheets/work2.mp4?ref=rss"> Video: | Source: /notes/css-sprite-sheets/work2.mp4 </video> <span>Behind the scenes</span><iframe height="414" scrolling="no" title="GSAP Draggable 360° sprite slider" src="https://codepen.io/jamiejefferson/embed/DgqxVe?default-tab=result&amp;theme-id=dark" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"> See the Pen <a href="https://codepen.io/jamiejefferson/pen/DgqxVe"> GSAP Draggable 360° sprite slider</a> by Jamie Jefferson (<a href="https://codepen.io/jamiejefferson">@jamiejefferson</a>) on <a href="https://codepen.io">CodePen</a>. </iframe><iframe height="333" scrolling="no" title="Steps Animation" src="https://codepen.io/simurai/embed/krWaKX?default-tab=result&amp;theme-id=dark" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"> See the Pen <a href="https://codepen.io/simurai/pen/krWaKX"> Steps Animation</a> by simurai (<a href="https://codepen.io/simurai">@simurai</a>) on <a href="https://codepen.io">CodePen</a>. </iframe>

2024/11/3
阅读更多

Centering a div in a div in 2020

<p>tl;dr: use <code>place-content: center</code> on a <code>grid</code> container.</p> <pre><code><span>.container</span> <span>{</span> <span>display</span><span>:</span> grid<span>;</span> <span>place-content</span><span>:</span> center<span>;</span> <span>}</span></code></pre> <p>Here’s how that looks like:</p> div child <p>That’s it. Two CSS rules.</p> <p>Yes, four years late according to <em>caniuse</em>. But this is apparently still not well-known today.</p> <p>Based on <a target="_blank" href="https://build-your-own.org/blog/20240813_css_vertical_center/">recent developments<span>🡵</span></a>, looks like we just need a few more years before we can finally get rid of the extra <code>display</code> rule so we can have the one CSS rule to center them all. </p>

2024/10/17
阅读更多

I made an app to fix my motion sickness

<p>Last May, Apple announced a new feature called <strong>Vehicle Motion Cues</strong> for their iOS devices. It’s an overlay that can help reduce motion sickness while riding a vehicle.</p> <p>I have really bad motion sickness, and riding cars, buses, and trains makes me nauseous. This feature would have been a nice relief for me, but as it stands, I use Android.</p> <p>Instead of buying a Malus fruit device, I took the matter into my own programmer hands. I created an alternative app for Android.</p> <p><span>To be sure, I checked the patents. Apple does have one regarding a certain motion sickness solution, but it’s specifically for head-mounted displays, not handheld devices. I figured it’s because there is prior art for handheld devices, such as <a target="_blank" href="https://play.google.com/store/apps/details?id=com.urbandroid.kinestop&amp;pcampaignid=web_share"><strong>KineStop</strong> for Android by Urbandroid<span>🡵</span></a>.</span></p> <p>My app is called <strong>EasyQueasy</strong>. What it does is display onscreen vestibular signals that try to help prevent motion sickness. This functions as an overlay that is displayed on top of whatever you’re doing, browsing the web or watching videos.</p> <video muted="" autoplay="" loop="" playsinline="" aria-label="demo video showing the app running in a moving train" alt="demo video showing the app running in a moving train" data-src="/notes/motion-sickness-app/demo.mp4"> <source src="https://leanrada.com/gen/_notes_motion_sickness_app_demo_664.generated.mp4?ref=rss"> Video: demo video showing the app running in a moving train | Source: /notes/motion-sickness-app/demo.mp4 </video> <p>The app is open source. I might try to get it on F-Droid someday. <small>Probably not Google Play since Google suspended my developer account due to “inactivity”.</small></p> <p>I made this for myself so I haven’t really sorted out distribution. <a target="_blank" href="https://github.com/Kalabasa/EasyQueasy">GitHub repo is here anyway<span>🡵</span></a>.</p> <p><span>Side note: Apologies to the RSS readers of this blog and <code>segfault</code>. I accidentally published this post in the RSS feed, but not on the site, which lead to an incomplete draft post and a 404 error.</span></p> <h2 id="how-does-it-work">How does it work?</h2> <p>There are two facets to this inquiry:</p> <ol> <li>How does it help with motion sickness?</li> <li>How does the app work behind the scenes?</li> </ol> <p>Let’s go over them sequentially.</p> <h2 id="how-to-unsick-motion">How to unsick motion</h2> <p><a target="_blank" href="https://en.wikipedia.org/wiki/Motion_sickness"><strong>Motion sickness</strong><span>🡵</span></a> happens when your brain receives conflicting motion cues from your senses. Motion cues come from the <strong>vestibular system</strong> (“sense of balance”, inner ears) and the <strong>visual system</strong> (eyes).</p> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="100%" data-placeholder="" caption="The system diagram" src="https://leanrada.com/notes/motion-sickness-app/1.png?ref=rss"> <span>The system diagram</span> <p>When you’re in a moving car, your eyes mostly see the stationary interiors of the car but your inner ears feel the movement. The signal of being stationary and the signal of being in motion does not make <em>sense</em> in the central processing system somewhere in the brain <em>(which decides that the solution to this particular situation is to vomit)</em>.</p> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="100%" data-placeholder="" caption="There’s a bug in the central processing module that triggers an unexpected response to conflicting signals." src="https://leanrada.com/notes/motion-sickness-app/2.png?ref=rss"> <span>There’s a bug in the central processing module that triggers an unexpected response to conflicting signals.</span> <p><span>Side note: The inverse happens in virtual reality when you move or rotate your character — your eyes recognise motion but your inner ears (your physical body) feel no motion, causing sickness. As I mentioned, I have really bad motion sickness, and both car movement and VR movement are almost unbearable to me.</span></p> <p>To reduce motion sickness, we need to match the visual input with the vestibular input. Unfortunately, there’s no hardware that can override the inner ears’ sense of motion just yet. But we can semi-hijack the visual system via age-old hardware called screens.</p> <p>By displaying movement on-screen that matches the actual movement of the body, motion sickness can be thus reduced!</p> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="100%" data-placeholder="" caption="The workaround is to hijack one of the sources to produce a ‘correct’ signal. In this case, we add an override to the visual input." src="https://leanrada.com/notes/motion-sickness-app/3.png?ref=rss"> <span>The workaround is to hijack one of the sources to produce a ‘correct’ signal. In this case, we add an override to the visual input.</span> <h2 id="behind-the-screens">Behind the screens</h2> <p>Almost every smartphone has an <strong>accelerometer</strong> in them. It’s a hardware module that measures <em>acceleration</em> in real time.</p> <p>Coincidentally, the inner ears also sense the same particular aspect of motion, <em>acceleration</em>. That’s why it can detect up or down (the planet’s gravity is an ever-present acceleration towards “down”), functioning as a “sense of balance”, and detect relative motion at the same time.</p> <p>So, smartphones have “inner ear” hardware. By reading off of this sensor, we can generate the correct visuals.</p> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="100%" data-placeholder="" caption="The visual layer is just an adapter to same acceleration data used by the other source. By converting motion data to visual data, we can be sure that the signals match! This is the final solution." src="https://leanrada.com/notes/motion-sickness-app/4.png?ref=rss"> <span>The visual layer is just an adapter to same acceleration data used by the other source. By converting motion data to visual data, we can be sure that the signals match! This is the final solution.</span><span> <p>The other Android app, KineStop, appear to use the phone’s magnetic sensor (compass) in addition to the accelerometer. So, it also knows about your absolute orientation in the world. It’s more effective in turning motions, but not so much for bumpy rides.</p> <p>There are definitely multiple ways to solve the problem, but I find that the purely acceleration-based solution is sufficient for me.</p> </span> <h2 id="dead-reckoning">Dead reckoning</h2> <p>The accelerometer provides acceleration data, but what we actually need to draw dots on the screen is <strong>position</strong>.</p> <p>Recall in calculus or physics that the integral of acceleration is the <em>velocity</em> and the integral of velocity is <em>position</em>. There are two layers of integration before we can draw dots on the screen.</p> <p><code>acceleration → velocity → position</code></p> <p>What the app does is <a target="_blank" href="http://lampx.tugraz.at/~hadley/physikm/apps/a2x.en.php"><strong>numerical integration</strong><span>🡵</span></a>. It keeps track of an estimated velocity and an estimated position in memory. Then, on every tick, it adds the current acceleration vector to the velocity vector, and add the velocity to the position. Note: all vectors here are in 3D. </p> <p>The calculated position vector provides an estimate of the phone’s current position! Finally, we can draw dots!</p> <p>To provide the correct sensation of movement, the dots must appear stable relative to the origin, that is, the earth. Negating the calculated position vector achieves this effect.</p> <pre><code><span>// pseudocode</span> <span>onEveryFrame</span><span>(</span><span>)</span> <span>{</span> velocity <span>+=</span> accelerometer<span>.</span><span>getAcceleration</span><span>(</span><span>)</span> position <span>+=</span> velocity <span>drawDots</span><span>(</span>offset <span>=</span> <span>-</span>position<span>)</span> <span>}</span></code></pre> <h2 id="error-correction">Error correction</h2> <p>The accelerometer signal is very <strong>noisy</strong>. While that is workable for many applications, when we’re doing position integration the errors really add up, and simple dead reckoning is way off.</p> <p>Standard signal processing techniques can be applied to deal with noise, like a low-pass filter.</p> <pre><code><span>// Example: With a low-pass filter</span> <span>onEveryFrame</span><span>(</span><span>)</span> <span>{</span> <span>// low-pass filter</span> smoothAcceleration <span>+=</span> <span>(</span>accelerometer<span>.</span><span>getAcceleration</span><span>(</span><span>)</span> <span>-</span> smoothAcceleration<span>)</span> <span>*</span> <span>0.60</span> <span>// position integration</span> velocity <span>+=</span> smoothAcceleration position <span>+=</span> velocity <span>drawDots</span><span>(</span>offset <span>=</span> <span>-</span>position<span>)</span> <span>}</span></code></pre> <p>But a simpler solution like dampening the velocity over time is plenty sufficient.</p> <pre><code><span>// Example: With velocity dampening</span> <span>onEveryFrame</span><span>(</span><span>)</span> <span>{</span> <span>// position integration</span> velocity <span>+=</span> accelerometer<span>.</span><span>getAcceleration</span><span>(</span><span>)</span> position <span>+=</span> velocity <span>// dampen</span> velocity <span>*=</span> <span>0.80</span> <span>drawDots</span><span>(</span>offset <span>=</span> <span>-</span>position<span>)</span> <span>}</span></code></pre> <p>And that is basically how EasyQueasy works. And presumably iOS does it similarly as well.</p> <h2 id="easyqueasy-features">EasyQueasy features</h2> <p>EasyQueasy’s features and advantages over the current iOS solution are:</p> <ul> <li>Configurable speed and other parameters.</li> <li>It’s 3D! Because movement in the real-world is three-dimensional!</li> <li>Option to activate the overlay from any screen via quick gesture shortcuts.</li> </ul> <h3 id="gallery">Gallery</h3> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="67.5%" data-placeholder="" alt="Screenshot of EasyQueasy features" src="https://leanrada.com/notes/motion-sickness-app/ss1.png?ref=rss"> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="67.5%" data-placeholder="" alt="Screenshot of EasyQueasy features" src="https://leanrada.com/notes/motion-sickness-app/ss2.png?ref=rss"> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="67.5%" data-placeholder="" alt="Screenshot of EasyQueasy features" src="https://leanrada.com/notes/motion-sickness-app/ss3.png?ref=rss"> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="67.5%" data-placeholder="" alt="Screenshot of EasyQueasy features" src="https://leanrada.com/notes/motion-sickness-app/ss5.png?ref=rss"> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="67.5%" data-placeholder="" alt="Screenshot of EasyQueasy features" src="https://leanrada.com/notes/motion-sickness-app/ss6.png?ref=rss">

2024/9/2
阅读更多

Stop using ease-out in your UIs!

<p><em>For RSS readers: This article contains interactive content available on the <a href="https://leanrada.com/notes/stop-using-ease-out?ref=rss">original post on leanrada.com</a>.</em></p> <p>Before anything, let me present you with a set of controls with no context.</p> <table> <tbody> <tr> <td> <button>Press me</button> </td> <td> <button>Press me</button> </td> </tr> <tr> <td> <input type="checkbox"> </td> <td> <input type="checkbox"> </td> </tr> </tbody> </table> <p><strong>Stop using ease-out, or ease-in-out, or whatever-out, in every UI animation!</strong></p> <p>There is a lot of propaganda on the internet against ease-in, saying that it's “unnatural”, “unusual”, or that it's “of the devil”. Some say that it's both “sluggish” and “abrupt”. Many pointing to ease-out as the safe, smooth, satisfying messiah of animation (including its safer kin, ease-in-out). There are even published ‘best practices’ which can be summed up to <a target="_blank" href="https://web.dev/articles/the-basics-of-easing">“just use ease-out”<span>🡵</span></a>. This post is here to set things straight — in a nonlinear fashion.</p> <p>So, why not ease-out? And what to use instead?</p> <h2 id="reason-1-its-overused">Reason #1. It’s overused</h2> <p>Let’s get the weakest point out of the way. Ease out is boring because it’s everywhere. Because it’s part of the browser default <code>ease</code> function (which is a combination of a fast ease-in with a slow ease-out). </p> <p>It’s like how corners are getting rounder and rounder on the web simply because it's easy and built-in.</p> <img loading="lazy" width="100%" data-placeholder="" src="https://leanrada.com/notes/stop-using-ease-out/figma-ease-out.gif?ref=rss" caption="Source: Figma"> <span>Source: Figma</span> <p>Is the public missing out on better web animations (and better corners) because of the ubiquity of these practices? Yes.</p> <h2 id="reason-2-its-unrealistic">Reason #2. It’s unrealistic</h2> <p>How about a study in skeuomorphism?</p> <p>Imagine a mechanical toggle switch. Something like the following video:</p> <iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/l-G_uejx0Rs?si=g-RJXjgqsY8euukl" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe> <p>Now here’s an interactive physics simulation of the same spring-based toggle switch:</p> <pre>Interactive content: <a href="https://leanrada.com/notes/stop-using-ease-out?ref=rss">Visit the website to play with interactive content!</a> Alternative text: interactive simulation of a mechanical switch</pre> <p><span><em>Drag your pointer horizontally and vertically across the simulation window to control the appendage that toggles the switch.</em></span></p> <p>This is a physics-based simulation with simulated forces, torques, and constraints. Let’s slow the simulation down and show some force lines for a better look.</p> <pre>Interactive content: <a href="https://leanrada.com/notes/stop-using-ease-out?ref=rss">Visit the website to play with interactive content!</a> Alternative text: interactive simulation of a mechanical switch</pre> <p>Play around with the simulation!</p> <p>Here’s what a typical motion looks like:</p> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="100%" data-placeholder="" src="https://leanrada.com/notes/stop-using-ease-out/typical.png?ref=rss" caption="Typical output graph of the above simulation"> <span>Typical output graph of the above simulation</span> <p>Notice the <span>position curve (red)</span>. <small>We'll get to the force curve later.</small> Does the position curve look anything familiar? Compare that with some standard easing functions:</p> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="100%" data-placeholder="" src="https://leanrada.com/notes/stop-using-ease-out/standard-easing.png?ref=rss" caption="Common easing functions. Source: MDN"> <span>Common easing functions. Source: MDN</span> <p><strong>The toggle switch’s position curve follows the ease-in curve!</strong> A slow start, gradually building up momentum, then finally stopping to a satisfying ‘click’!</p> <p>Contrary to “best practice”, the natural motion of a toggle switch does not follow an ease-out nor an ease-in-out curve! I’ll go further and say that ease-out is unnatural for any kind of UI control that represents a tactile interaction.</p> <p>Like buttons. In the real world, buttons (the ones that are nice to press anyway) have some kind of buckling mechanism when pressed. Similar to the toggle switch example above, there’s a buildup, a fall, and a final ‘click’ into place.</p> <video muted="" autoplay="" loop="" playsinline="" caption="A button. In the real world." data-src="/notes/stop-using-ease-out/button.mp4"> <source src="https://leanrada.com/notes/stop-using-ease-out/button.mp4?ref=rss"> Video: | Source: /notes/stop-using-ease-out/button.mp4 </video> <span>A button. In the real world.</span> <p>Best practice says that the sharp stop is unnatural and should be avoided. But that well-defined resolution is part of what makes switches and buttons feel good. Just imagine a button that dampens the motion the more you press it. It’ll feel squishy and mushy. You know what else slows down the further you go? Ease-out!</p> <p>And yet, thousands of UIs still use ease out for UI controls!</p> <video muted="" autoplay="" loop="" playsinline="" caption="Ease out everywhere. One of these is a skeuomorphic rocker switch, ironically." data-src="/notes/stop-using-ease-out/examples.mp4"> <source src="https://leanrada.com/notes/stop-using-ease-out/examples.mp4?ref=rss"> Video: | Source: /notes/stop-using-ease-out/examples.mp4 </video> <span>Ease out everywhere. One of these is a skeuomorphic rocker switch, ironically.</span> <p>Has abstract UI design gone too far?</p> <h3 id="counterexamples">Counterexamples</h3> <p>Of course, not all UI animations need ease in, such as macro interactions, card movement, scrolling, expand/collapse, or any object that ‘animate themselves’ as opposed to raw manipulation.</p> <video muted="" autoplay="" loop="" playsinline="" caption="Source: Material Design" data-src="/notes/stop-using-ease-out/material-pager.mp4"> <source src="https://leanrada.com/notes/stop-using-ease-out/material-pager.mp4?ref=rss"> Video: | Source: /notes/stop-using-ease-out/material-pager.mp4 </video> <span>Source: Material Design</span> <p>Unlike switches and buttons, these things don’t have a frame or housing that can immediately stop them when they ‘bottom out’. So they have to decelerate naturally.</p> <p>In the end, it depends. But as a general rule? Ease-in for tactile things.</p> <h2 id="reason-3-ease-in-is-more-satisfying">Reason #3. Ease-in is more satisfying</h2> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="100%" data-placeholder="" src="https://leanrada.com/notes/stop-using-ease-out/typical.png?ref=rss" caption="The graph again. This time focus on the force curve."> <span>The graph again. This time focus on the force curve.</span> <p>If you’re a mechanical keyboard enthusiast, you might’ve recognised the general landmarks in the <span>force curve (blue)</span> above. That ‘tactile bump’ and subsequent drop in force is a big part of what provides the satisfying feedback that mechanical keys are known for (and coincidentally, produces the ease-in motion).</p> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="100%" data-placeholder="" src="https://leanrada.com/notes/stop-using-ease-out/cherry.png?ref=rss" caption="Force curve of a tactile keyboard switch. Source: deskthority.net"> <span>Force curve of a tactile keyboard switch. Source: deskthority.net</span> <p>But <em>why</em> is it satisfying?</p> <p><audio controls="" src="/notes/stop-using-ease-out/vsauce.mp3"></audio></p> <p>I present my sub-thesis:</p> <blockquote> <p><strong>The sequence of tension and release is intrinsically satisfying.</strong> <cite>me</cite> </p> </blockquote> <p>I will now present supporting evidence with xkcd-style graphs.</p> <h3 id="a-popping-bubble-wraps">A. Popping bubble wraps</h3> <p><span><strong>Disclaimer:</strong> We are getting into subjective and pseudoscience territory.</span></p> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="100%" data-placeholder="" src="https://leanrada.com/notes/stop-using-ease-out/bubble-wrap.png?ref=rss" caption="Estimated force curve of popping a bubble in a bubble wrap."> <span>Estimated force curve of popping a bubble in a bubble wrap.</span> <p>Popping bubble wraps is a satisfying sensation. Popping a bubble in a bubble wrap follows the familiar buildup, release, and resolution pattern that is associated with ease-in.</p> <p><small><a target="_blank" href="https://xkcd.com/2458/">Relevant xkcd.<span>🡵</span></a></small></p> <h3 id="b-scratching-an-itch">B. Scratching an itch</h3> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="100%" data-placeholder="" src="https://leanrada.com/notes/stop-using-ease-out/itch.png?ref=rss" caption="Estimated discomfort experienced during an itch’s lifetime."> <span>Estimated discomfort experienced during an itch’s lifetime.</span> <p>While the act of scratching by itself is mildly pleasurable, when paired with an itchy skin, it becomes a satisfying experience.</p> <h3 id="c-music-theory">C. Music theory</h3> <p>🚧 This section is WIP, something about dissonance and consonance 🚧</p> <h3 id="d-arousal-jag-theory">D. Arousal jag theory</h3> <blockquote> <p>The abrupt fall from elevated levels of arousal to a lower, more appropriate level is thought to produce a pleasurable response. <cite>APA Dictionary of Psychology, <a target="_blank" href="https://dictionary.apa.org/arousal-jag">‘arousal jag’<span>🡵</span></a></cite> </p> </blockquote> <p>This theory was introduced by a psychologist named Daniel E. Berlyne in 1970. The idea is that an increase in tension followed by a sharp decrease produces a satisfying feeling. This framework works well for ease-in’s case in the context of the force curve, or more directly, with ease-in’s initial slow buildup followed by its abrupt resolution.</p> <p>If you’re looking for the psychology behind microinteractions, well, that’s one of them.</p> <h3 id="e-the-three-act-structure">E. The Three-Act Structure</h3> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="100%" data-placeholder="" src="https://leanrada.com/notes/stop-using-ease-out/3act.png?ref=rss" caption="The three-act structure with a tension graph."> <span>The three-act structure with a tension graph.</span> <p>In storytelling or filmmaking, the <a target="_blank" href="https://tvtropes.org/pmwiki/pmwiki.php/Main/ThreeActStructure">three-act structure<span>🡵</span></a> is a model for analysing or creating good stories. It mirrors the tension-resolution sequence in a grander scale. And with bigger scope comes the potential for a higher level of satisfaction — <em>catharsis</em>. Great stories that use the three-act structure always leave you in a state of catharsis.</p> <p>Is it possible to have micro-catharses in UI animations?</p> <hr> <p>All of these patterns of satisfaction reflect ease-in’s slow buildup and sudden resolution. While there are other kinds of satisfying phenomenon, like <a target="_blank" href="https://youtu.be/3clqk2U3T9Y">sand slicing<span>🡵</span></a> which has nothing to do with any of this; tension and release is a way to induce the positive effect.</p> <p>There must be a balance to the proportion of tension and release, else the negative effects of the tension may overcome the positive effects of release, or the tension too light that the release is too shallow. A bubble wrap that is really hard to pop would be quite annoying, and a grand story that ends prematurely would be disappointing. The easing curve must be manually finetuned depending on the purpose. For buttons and toggles, keep it shorter than a bubble wrap pop.</p> <p><span>🫰 <strong>Snap!</strong> Try snapping your fingers. Did you do it? If so, you just made an ease-in motion. <a target="_blank" href="https://www.youtube.com/watch?v=4DHQ4mKSZXU">Don’t believe me?<span>🡵</span></a> When people say they want <em>snappy</em> animations, what do they really want?</span></p> <h2 id="tips">Tips</h2> <ul> <li>Use a custom curve appropriate to the size of the element. Don’t use the default ease-in because it’s likely to feel too slow for most use cases. In CSS, there’s <a target="_blank" href="https://cubic-bezier.com/"><code>cubic-bezier()</code><span>🡵</span></a> for example.</li> <li>Use a duration appropriate to the curve, the size of the element, and the scale of the movement.</li> <li>Partially start the animation on pointer press, not on release.<ul> <li>For mouse users, there’s already an initial ‘actuation force’ required to trigger the mouse button. In these cases, the initial velocity shouldn’t be zero (the easing curve shouldn’t start at a horizontal slope). For touchscreens, there’s no such actuation force.</li> </ul> </li> </ul> <h2 id="conclusion">Conclusion</h2> <p>Don’t use ease-out in everything! Try ease-in! Or try a combination of both with varying weights! Just try anything at all. Tweak your curves as often as you tweak your paddings. </p>

2024/7/15
阅读更多

Creating a halftone effect with CSS

<p><em>For RSS readers: This article contains interactive content available on the <a href="https://leanrada.com/notes/pure-css-halftone?ref=rss">original post on leanrada.com</a>.</em></p> <p>Here’s a quick halftone effect (i.e. a retro printed look) using CSS with only one div at the minimum.</p> <p>First of all, here’s a live demo:</p> <pre>Interactive content: <a href="https://leanrada.com/notes/pure-css-halftone?ref=rss">Visit the website to play with interactive content!</a> Alternative text: CSS halftone demo</pre> <label> Toggle effect <input type="checkbox" checked="" onchange="onChangeHalftoneDemoEnabled(event)"> </label> <p>Toggle the filter class using the checkbox above.</p> <p>To further illustrate the halftone effect, the following demo can vary the <strong>size</strong> of the dots and the degree to which they <strong>‘bleed’</strong>:</p> <pre>Interactive content: <a href="https://leanrada.com/notes/pure-css-halftone?ref=rss">Visit the website to play with interactive content!</a> Alternative text: CSS halftone demo</pre> <label> Size <input type="range" min="4" max="20" value="12" step="1" oninput="onInputHalftoneDemoSize(event)"> </label><label> Bleed <input type="range" min="0.1" max="0.45" value="0.2" step="0.01" oninput="onInputHalftoneDemoBleed(event)"> </label> <p>There are several ways to do this in CSS. The above is a bit more advanced with 2-3 extra divs. I’ll try to show a simple method, first.</p> <h2 id="halftone-basics">Halftone basics</h2> <p>To keep it simple, let’s start with a black-and-white image. It should be easy to layer in additional colors with the same principle.</p> <pre>Interactive content: <a href="https://leanrada.com/notes/pure-css-halftone?ref=rss">Visit the website to play with interactive content!</a> Alternative text: CSS halftone demo</pre> <p>Actually, let’s start with a simpler image. A gradient, to illustrate how halftone works in the first place.</p> <pre>Interactive content: <a href="https://leanrada.com/notes/pure-css-halftone?ref=rss">Visit the website to play with interactive content!</a> Alternative text: CSS halftone demo</pre> <label> Size <input type="range" min="4" max="20" value="5" step="1" oninput="onInputHalftoneDemoSize(event)"> </label><label> Bleed <input type="range" min="0.1" max="2" value="1.5" step="0.01" oninput="onInputHalftoneDemoBleed(event)"> </label> <p>A <a target="_blank" href="https://en.wikipedia.org/wiki/Halftone">halftone<span>🡵</span></a> pattern is an array of ink dots simulating the appearance of smooth gradiation of tones using just two pure tones (pure black ‘ink’ and pure white background in this case). By varying the size of the dots, the average ink coverage in a given area determines how light or dark the tone is in that area.</p> <p>Dots large enough would bleed into each other, creating the effect of negative dots.</p> <h2 id="screen-and-threshold">Screen and threshold</h2> <p>Dot size and bleed can be emulated in one go using two simple image processing operations, <strong>screen</strong> and <strong>threshold</strong>.</p> <p>The first step is to <em>screen</em> the source image (in this case, the gradient) with a <em>blurry dot matrix pattern</em>.</p> <p><a target="_blank" href="https://en.wikipedia.org/wiki/Blend_modes#Screen"><strong>Screen</strong><span>🡵</span></a> is an operation that mixes the pixels of the source image and the overlay image using some kind of an inverted multiplication formula. Essentially, it <em>lightens lighter areas multiplicatively</em>.</p> <p>Because the dots are blurry (i.e. having feathered edges), the screen operation gives us smaller-looking dots in lighter areas on the original image and denser dots in darker areas — exactly what we want in halftone.</p> <p>This operation is done via CSS <code>mix-blend-mode: screen</code>.</p> <p>The blurry dot pattern is generated using a <code>radial-gradient</code> as a repeated <code>background-image</code>, like this:</p> <pre><code><span>background-image</span><span>:</span> <span>radial-gradient</span><span>(</span>14px at 50% 50%<span>,</span> black<span>,</span> white<span>)</span><span>;</span> <span>background-size</span><span>:</span> 20px 20px<span>;</span></code></pre> <p>The next step is to <a target="_blank" href="https://en.wikipedia.org/wiki/Thresholding_(image_processing)"><strong>threshold</strong><span>🡵</span></a> the resulting image. That is, convert the image into pure black &amp; pure white pixels. Dark pixels become fully black, and light pixels become white — according to some defined <em>threshold</em> between light vs dark.</p> <p>This creates the signature black-ink-matrix-on-white-paper look.</p> <p>In CSS, there is no threshold filter, but it can be simulated by applying an extremely high <strong>contrast</strong> filter, pushing pixel values to the extremes of pure white and pure black. Effectively the same result as thresholding. In code, that’s simply a <code>filter: contrast(999)</code>.</p> <p>Another thing we can add is a <strong>blur</strong> filter, <em>just before</em> the thresholding operation. This emulates surface tension of the ink, or something.</p> <p>Let’s take a moment to look at the basic black-and-white solution so far:</p> <pre><code><span><span><span>&lt;</span>div</span> <span>class</span><span><span>=</span><span>"</span>halftone<span>"</span></span><span>&gt;</span></span> <span><span><span>&lt;</span>img</span> <span>src</span><span><span>=</span>...</span><span>&gt;</span></span> <span><span><span>&lt;/</span>div</span><span>&gt;</span></span> <span><span><span>&lt;</span>style</span><span>&gt;</span></span><span><span> <span>.halftone</span> <span>{</span> <span>position</span><span>:</span> relative<span>;</span> <span>/* brightness controls the threshold point */</span> <span>filter</span><span>:</span> <span>brightness</span><span>(</span>0.8<span>)</span> <span>blur</span><span>(</span>3px<span>)</span> <span>contrast</span><span>(</span>999<span>)</span><span>;</span> <span>}</span> <span>.halftone::after</span> <span>{</span> <span>position</span><span>:</span> absolute<span>;</span> <span>inset</span><span>:</span> 0<span>;</span> <span>background</span><span>:</span> <span>radial-gradient</span><span>(</span>10px at center<span>,</span> black<span>,</span> white<span>)</span><span>;</span> <span>background-size</span><span>:</span> 20px 20px<span>;</span> <span>mix-blend-mode</span><span>:</span> screen<span>;</span> <span>}</span> </span></span><span><span><span>&lt;/</span>style</span><span>&gt;</span></span></code></pre> <h2 id="colours-may-yeet-knowingly">Colours may yeet knowingly</h2> <p>When you get the black ink dots going, adding the rest of the colours is easy. Just add a set of dots for each of CMY — cyan, magenta, and yellow, the “primary colours” of ink — to complete the <a target="_blank" href="https://en.wikipedia.org/wiki/CMYK_color_model">CMYK<span>🡵</span></a>! Make sure to stagger the dots so they are distributed evenly. How to stagger them well is left as an exercise to the dear reader, you (see <a target="_blank" href="https://the-print-guide.blogspot.com/2009/05/halftone-screen-angles.html">halftone angles<span>🡵</span></a>, <a target="_blank" href="https://proofing.de/whats-moire-and-can-i-see-moire-in-a-proof/">moiré patterns<span>🡵</span></a>, etc).</p> <pre><code><span>background</span><span>:</span> <span>radial-gradient</span><span>(</span>10px at center<span>,</span> #000<span>,</span> white<span>)</span><span>,</span> <span>radial-gradient</span><span>(</span>10px at ...<span>,</span> #0ff<span>,</span> white<span>)</span><span>,</span> <span>radial-gradient</span><span>(</span>10px at ...<span>,</span> #f0f<span>,</span> white<span>)</span><span>,</span> <span>radial-gradient</span><span>(</span>10px at ...<span>,</span> #ff0<span>,</span> white<span>)</span><span>;</span></code></pre> <p>These additional layers will work just as well as black because the <code>contrast</code> filter operates on each RGB channel independently. The colours of cyan <span>(#0ff)</span>, magenta <span>(#f0f)</span>, and yellow <span>(#ff0)</span> are at their own extremes in each RGB channel, just like black (#000) and white (#fff). Thus, the <code>contrast</code> filter produces a similar thresholding effect on each colour in CMYK independently and simultaneously!</p> <p><span>Note: This is not a very accurate representation of halftone, mainly due to the operations being in RGB, not CMY. An accurate simulation would be to apply thresholding to each channel in some CMY space via JS or maybe WebGL. But this shallow emulation may look good enough in many cases.</span></p> <p>Here’s the result…?</p> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="100%" data-placeholder="" src="https://leanrada.com/notes/pure-css-halftone/no-ink-mixing.png?ref=rss" alt="halftone effect with only magenta dots visible"> <p>Only magenta is showing, because the magenta layer is the top layer in that <code>background-image</code> list! The other layers are hidden beneath the magenta layer. We need to combine these layers to see all the colours.</p> <p>In order to mix the four layers of ‘ink’ correctly, you must use the <strong>multipy</strong> blend mode to simulate how inks mix together (i.e. <a target="_blank" href="https://en.wikipedia.org/wiki/Subtractive_color">subtractive colour mixing<span>🡵</span></a>).</p> <p>Since we’re mixing <code>background-image</code>s together, we use this property: <code>background-blend-mode: multiply</code>.</p> <pre>Interactive content: <a href="https://leanrada.com/notes/pure-css-halftone?ref=rss">Visit the website to play with interactive content!</a> Alternative text: CSS halftone demo</pre> <label> Size <input type="range" min="4" max="20" value="5" step="1" oninput="onInputHalftoneDemoSize(event)"> </label> <p>Aaand that’s it! A simple Halftone effect with a single div wrapper!</p> <p>This simple filter is not very robust, so you may want to tailor the brightness and saturation levels of the particular source image.</p> <pre><code><span><span><span>&lt;</span>div</span> <span>class</span><span><span>=</span><span>"</span>halftone<span>"</span></span><span>&gt;</span></span> <span><span><span>&lt;</span>img</span> <span>src</span><span><span>=</span>...</span><span>&gt;</span></span> <span><span><span>&lt;/</span>div</span><span>&gt;</span></span> <span><span><span>&lt;</span>style</span><span>&gt;</span></span><span><span> <span>.halftone</span> <span>{</span> <span>position</span><span>:</span> relative<span>;</span> <span>filter</span><span>:</span> <span>brightness</span><span>(</span>0.8<span>)</span> <span>blur</span><span>(</span>3px<span>)</span> <span>contrast</span><span>(</span>999<span>)</span><span>;</span> <span>}</span> <span>.halftone::after</span> <span>{</span> <span>position</span><span>:</span> absolute<span>;</span> <span>inset</span><span>:</span> 0<span>;</span> <span>background</span><span>:</span> <span>radial-gradient</span><span>(</span>10px at center<span>,</span> black<span>,</span> white<span>)</span><span>,</span> <span>radial-gradient</span><span>(</span>10px at 5px 5px<span>,</span> cyan<span>,</span> white<span>)</span><span>,</span> <span>radial-gradient</span><span>(</span>10px at 15px 5px<span>,</span> magenta<span>,</span> white<span>)</span><span>,</span> <span>radial-gradient</span><span>(</span>10px at 10px 15px<span>,</span> yellow<span>,</span> white<span>)</span><span>;</span> <span>background-size</span><span>:</span> 20px 20px<span>;</span> <span>background-blend-mode</span><span>:</span> multiply<span>;</span> <span>mix-blend-mode</span><span>:</span> screen<span>;</span> <span>}</span> </span></span><span><span><span>&lt;/</span>style</span><span>&gt;</span></span></code></pre> <p><span>A minor point, but the demo above actually uses two separate overlay divs instead of a single div. This is to achieve better dot staggering.</span></p> <h2 id="variations">Variations</h2> <p>Notice anything wrong with the last image above? There’s an unexpected pattern on the magenta in that flower petal. It should be a neat grid matrix, not this weird smiley face pattern or whatever it is. Even worse, the amount of ink is not correctly in proportion to the original image’s colour — There are more magenta dots than expected!</p> <img srcset="" sizes="" spec="100% [664) 664" loading="lazy" width="100%" data-placeholder="" src="https://leanrada.com/notes/pure-css-halftone/too-many-magenta.png?ref=rss" alt="too many magenta dots (zoomed-in)"> <p>Apparently, the black dots were turning into the coloured ones. I think the problem was that: <strong><span>coloured source image</span> ⊕ black dot pattern = <span>coloured dots</span></strong>, <em>where the symbol ⊕ represents the screen-threshold operation.</em> In other words, colour is contagious!</p> <p>What I did to fix this was separate the K layer (black) from CMY, and have it use its own greyscale copy of the source image. <strong>greyscale source image ⊕ black dot pattern = black dots</strong>.</p> <p>Here’s a vivid example where you can toggle the ‘separate-K’ version for comparison purposes:</p> <pre>Interactive content: <a href="https://leanrada.com/notes/pure-css-halftone?ref=rss">Visit the website to play with interactive content!</a> Alternative text: CSS halftone demo</pre> <label> Toggle effect <input type="checkbox" checked="" onchange="onChangeHalftoneDemoEnabled(event)"> </label><label> Separate K layer <input type="checkbox" onchange="onChangeHalftoneDemoSeparateK(event)"> </label><label> Size <input type="range" min="4" max="20" value="7" step="1" oninput="onInputHalftoneDemoSize(event)"> </label><label> Bleed <input type="range" min="0.1" max="0.45" value="0.35" step="0.01" oninput="onInputHalftoneDemoBleed(event)"> </label><label> Rotation <input type="range" min="0" max="360" value="0" step="0.1" oninput="onInputHalftoneDemoRotation(event)"> </label> <p>There are more ways to go about this with different qualities and levels of realism and complexity. Like dithering.</p> <p>I think the initial single-div solution is actually fine as long as you tweak the source image to be more readable under the filter.</p> <h2 id="gallery">Gallery</h2> <p>To finish with, here are a more demos!</p> <pre>Interactive content: <a href="https://leanrada.com/notes/pure-css-halftone?ref=rss">Visit the website to play with interactive content!</a> Alternative text: CSS halftone demo</pre> <label> Toggle effect <input type="checkbox" checked="" onchange="onChangeHalftoneDemoEnabled(event)"> </label><label> Separate K layer <input type="checkbox" checked="" onchange="onChangeHalftoneDemoSeparateK(event)"> </label><label> Size <input type="range" min="4" max="20" value="5" step="1" oninput="onInputHalftoneDemoSize(event)"> </label><label> Bleed <input type="range" min="0.1" max="0.45" value="0.4" step="0.01" oninput="onInputHalftoneDemoBleed(event)"> </label><label> Rotation <input type="range" min="0" max="360" value="0" step="0.1" oninput="onInputHalftoneDemoRotation(event)"> </label> <pre>Interactive content: <a href="https://leanrada.com/notes/pure-css-halftone?ref=rss">Visit the website to play with interactive content!</a> Alternative text: CSS halftone demo</pre> <label> Toggle effect <input type="checkbox" checked="" onchange="onChangeHalftoneDemoEnabled(event)"> </label><label> Separate K layer <input type="checkbox" checked="" onchange="onChangeHalftoneDemoSeparateK(event)"> </label><label> Size <input type="range" min="4" max="20" value="5" step="1" oninput="onInputHalftoneDemoSize(event)"> </label><label> Bleed <input type="range" min="0.1" max="0.45" value="0.4" step="0.01" oninput="onInputHalftoneDemoBleed(event)"> </label><label> Rotation <input type="range" min="0" max="360" value="0" step="0.1" oninput="onInputHalftoneDemoRotation(event)"> </label> <pre>Interactive content: <a href="https://leanrada.com/notes/pure-css-halftone?ref=rss">Visit the website to play with interactive content!</a> Alternative text: CSS halftone demo</pre> <label> Toggle effect <input type="checkbox" checked="" onchange="onChangeHalftoneDemoEnabled(event)"> </label><label> Separate K layer <input type="checkbox" checked="" onchange="onChangeHalftoneDemoSeparateK(event)"> </label><label> Size <input type="range" min="4" max="20" value="5" step="1" oninput="onInputHalftoneDemoSize(event)"> </label><label> Bleed <input type="range" min="0.1" max="0.45" value="0.4" step="0.01" oninput="onInputHalftoneDemoBleed(event)"> </label><label> Rotation <input type="range" min="0" max="360" value="0" step="0.1" oninput="onInputHalftoneDemoRotation(event)"> </label> <pre>Interactive content: <a href="https://leanrada.com/notes/pure-css-halftone?ref=rss">Visit the website to play with interactive content!</a> Alternative text: CSS halftone demo</pre> <label> Toggle effect <input type="checkbox" checked="" onchange="onChangeHalftoneDemoEnabled(event)"> </label><label> Separate K layer <input type="checkbox" checked="" onchange="onChangeHalftoneDemoSeparateK(event)"> </label><label> Size <input type="range" min="4" max="20" value="5" step="1" oninput="onInputHalftoneDemoSize(event)"> </label><label> Bleed <input type="range" min="0.1" max="0.45" value="0.4" step="0.01" oninput="onInputHalftoneDemoBleed(event)"> </label><label> Rotation <input type="range" min="0" max="360" value="0" step="0.1" oninput="onInputHalftoneDemoRotation(event)"> </label> <p>P.S. Please don’t look at the demos’ source code. It’s terrible. </p>

2024/5/5
阅读更多