<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Aimantas]]></title><description><![CDATA[Aimantas]]></description><link>https://aimantas.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sat, 14 Nov 2026 19:21:57 GMT</lastBuildDate><atom:link href="https://aimantas.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[I spent 1,200 Higgsfield credits in 56 minutes. Here's what I learned]]></title><description><![CDATA[Hi, I'm Aimantas. I build small web products and the videos that go with them. On 5 November I bought Higgsfield's Plus plan, 1,200 credits for €59, and 56…]]></description><link>https://aimantas.hashnode.dev/i-spent-1-200-higgsfield-credits-in-56-minutes-here-s-what-i-learned</link><guid isPermaLink="true">https://aimantas.hashnode.dev/i-spent-1-200-higgsfield-credits-in-56-minutes-here-s-what-i-learned</guid><category><![CDATA[AI]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[video]]></category><dc:creator><![CDATA[Aimantas]]></dc:creator><pubDate>Fri, 13 Nov 2026 15:19:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ae8f32c72ffc4004e31d301/5ee8b43c-e24c-4201-96ab-5e94f1247c25.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi, I'm Aimantas. I build small web products and the videos that go with them. On 5 November I bought Higgsfield's Plus plan, 1,200 credits for €59, and 56 minutes later 96.6% of them were gone.</p>
<p>Nobody hacked anything. Four of my working sessions, spread across three projects, reached into the same balance at the same moment, and none of them had a limit. By 20:51 I had 41 credits left and a fairly clear lesson about how AI video gets expensive.</p>
<p><img src="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/tcm8hai0jn55muzaddx3.png" alt="The Higgsfield balance falling from 1,200 to 41 credits in under an hour, with four big steps labelled" /></p>
<h2>The first step down was 512 credits in one second</h2>
<p>The chart is the whole story in one line. At 20:09 four Seedance 2.5 clips were submitted together, each seven seconds at 1080p, and the balance dropped by 512 in the same second. Those were concept loops for a website hero: moving backgrounds nobody had picked yet.</p>
<p>Thirteen minutes later two lip-sync clips went through for a re-voiced YouTube video, 217 credits for 31 seconds of a talking presenter. At 20:35 another session fired off five Kling clips and one more Seedance clip in a single batch for a second website. At 20:46 someone wanted one more 1080p loop, and that was the plan done.</p>
<p>Each of those decisions looked reasonable inside its own session. Nobody was watching all four at once, and the balance is the only place they meet.</p>
<h2>What €59 actually bought</h2>
<p>Here's the honest list. The website hero concepts took 689.5 credits, about €34: fourteen still images and five seven-second video loops. The second website's hero video took 183.5, about €9. The re-voiced Transport Fever video took 269, about €13, and eight voice takes for my "Guess the flag" Shorts took 13.8, which is less than a euro.</p>
<p>The cheap things were the ones I actually shipped. A whole 7:34 narration in ElevenLabs, 53 lines, cost about 37 credits. That's under €2 for the entire voice track of a seven-and-a-half-minute video. Draft images were a quarter of a credit each. I'd happily pay that all day.</p>
<p>The expensive things were mostly questions. A concept loop for a hero section is a way of asking "does this direction feel right?" I paid finished-asset prices for answers I hadn't needed yet.</p>
<p><img src="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/uwj5p5fq18q6l6rl6tlr.png" alt="Credits per job: Grok Lite 5, Seedance draft 15, Seedance 720p 35, a full narration 37, Seedance 1080p 128" /></p>
<p>The gap between those bars is where the money went. On the same day a seven-second Seedance clip at 1080p cost me 128 credits, about €6.30, a five-second Grok Lite clip at 720p was quoted at 5. A 480p Seedance draft was 15. For a background loop that hadn't been approved, the 5-credit clip would have answered exactly the same question.</p>
<h2>The presenter that didn't pay for itself</h2>
<p>The one result I can measure is the Transport Fever video. I'd already published part 2 of the series with plain voice-over gameplay. For the re-voice I added a lip-synced presenter who opens and closes the video, then shrinks into a corner window while the game plays.</p>
<p><img src="https://aimantas.com/writing/higgsfield-credits-honest-review/assets/revoice-pip-frame.jpg" alt="A frame from the re-voiced video: the Transport Fever map with a " /></p>
<p>That corner window is 217 credits. It looked natural, honestly better than I expected. It just didn't move anything. The re-voiced upload had 41 views after three days, and it's no longer on YouTube. The original upload of the same story, plain voice over gameplay, had about 2,400 views after its first three days and 2,759 when I checked on 13 November.</p>
<p>That isn't a perfectly fair fight: the re-voice was a second upload of a story the channel had already told. But it's enough to say the presenter didn't earn its cost, and that a €2 narration over real footage was already the right product.</p>
<h2>Why it happened, in three plain reasons</h2>
<p><strong>One balance, no budgets.</strong> Higgsfield has a single credit pool. There's no per-project limit and no spend cap on the connector, so a session working on a website hero and a session working on a YouTube video are spending from the same wallet without knowing about each other.</p>
<p><strong>Defaults decide the bill.</strong> The generation skill bundled with the connector tells agents to prefer the quality default and skip cost estimates unless asked. So every busy session reached for Seedance at 1080p. Nobody chose to spend €6.30 per clip. The default did.</p>
<p><strong>Batches before samples.</strong> Four Seedance clips went in one go, and five Kling clips in another. If the first clip had been reviewed before the rest were sent, most of that spend would have stopped at one.</p>
<p>Credits make this easy to miss. "128" feels like a small number in a tool output. "€6.30 for seven seconds of background" reads very differently, and I'd have asked whether I really needed it.</p>
<h2>What I liked about the tool</h2>
<p>To be fair to Higgsfield, the tool did exactly what it was asked. The connector works inside Claude Code with about 150 tools, and every cost check I ran matched the charge to the credit. ElevenLabs voice through it was quick and good: lines cost between 0.46 and 1.15 credits. When a batch hit rate limits, 8 of my 53 lines failed, and those failures weren't charged. The lip-sync quality was good enough that I shipped it.</p>
<p>The problem wasn't the product. It was how I let it be used.</p>
<h2>What I changed</h2>
<p>Every video job now starts with a price check. Then one cheap sample. Only after someone has looked at the sample does a batch run, and 1080p needs an explicit ask.</p>
<p>The ladder starts at Grok Lite (5 credits for five seconds) and a 480p Seedance draft (15). Lip-sync starts at 480p too: the same 12 seconds of voice quoted 39 credits at 480p against 91 at 720p. The presenter spent most of his screen time in a window about 480 pixels wide anyway.</p>
<p>Since then my "Guess the flag" Shorts have moved off paid generation entirely: local voice, local Blender renders, no credits at all. That's what happens when you price a thing honestly. Some of it turns out not to need the expensive tool in the first place.</p>
<h2>Would I buy it again?</h2>
<p>For voice and draft images, yes, without thinking. They were cheap, fast and good enough to ship.</p>
<p>For video, only with a limit set per job before anything runs, and only one session at a time on the balance. The lesson I'm keeping is simpler than the chart: decide what a question is worth before you ask it. A concept loop is worth 5 credits. A finished hero someone has approved might be worth 128. Paying the second price for the first job is how €59 disappears in under an hour.</p>
<hr />
<p><em>The full version with the itemised table and sources is on <a href="https://aimantas.com/writing/higgsfield-credits-honest-review/?utm_source=hashnode&amp;utm_medium=community&amp;utm_campaign=higgsfield-review">aimantas.com</a>. Numbers come from the account's transaction list (152 rows, read 7 November 2026) and cost quotes taken on 5 November. Project attribution comes from file times and session records, since Higgsfield doesn't tag jobs by project. Video views were read from public YouTube pages.</em></p>
]]></content:encoded></item><item><title><![CDATA[CSS view transitions in Laravel and Livewire]]></title><description><![CDATA[Hi, I'm Aimantas. I build small web products, and the bit I've enjoyed most lately is tiny: tap a tile in Ball Quiz and it grows into the game board…]]></description><link>https://aimantas.hashnode.dev/css-view-transitions-in-laravel-and-livewire</link><guid isPermaLink="true">https://aimantas.hashnode.dev/css-view-transitions-in-laravel-and-livewire</guid><category><![CDATA[CSS]]></category><category><![CDATA[Laravel]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Aimantas]]></dc:creator><pubDate>Mon, 02 Nov 2026 20:49:20 GMT</pubDate><content:encoded><![CDATA[<p>Hi, I'm Aimantas. I build small web products, and the bit I've enjoyed most lately is tiny: tap a tile in <a href="https://ball-quiz.com/">Ball Quiz</a> and it grows into the game board. Leave, and it shrinks back into the tile. The morph is plain CSS, on a Laravel and Livewire app.</p>
<p>It also stopped working on phones because of a two-pixel overflow, which is a story I'll get to. First, the fun part.</p>
<h2>One rule turns page loads into transitions</h2>
<p>Ball Quiz pages are ordinary documents. One at-rule asks the browser to animate between them, and a media query switches it off for people who ask for less motion:</p>
<pre><code class="language-css">@view-transition { navigation: auto; }

@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}
</code></pre>
<p>By default the browser snapshots the old page, loads the new one and crossfades. Browsers without support just load the page, so nothing is lost. I tuned it so leaving is quick (140ms) and arriving is a bit slower (260ms) with an 8px rise, which reads as "new page" without feeling like a slideshow:</p>
<pre><code class="language-css">::view-transition-old(root) { animation: 140ms cubic-bezier(.4, 0, 1, 1) both bq-vt-page-out; }
::view-transition-new(root) { animation: 260ms cubic-bezier(.16, 1, .3, 1) both bq-vt-page-in; }
@keyframes bq-vt-page-out { to { opacity: 0; } }
@keyframes bq-vt-page-in { from { opacity: 0; transform: translateY(8px); } }
</code></pre>
<h2>Give the moving thing a name</h2>
<p>The tile-to-board morph is one idea: put the same <code>view-transition-name</code> on an element on each page and the browser animates position and size between them. A name has to be unique on the page when the snapshot is taken, and the lobby shows lots of tiles, so every game gets its own. There are nine game modes, plus the daily and the sticker album, so eleven names. Here's Sprint:</p>
<pre><code class="language-css">.bq-play-tile[data-game="sprint"],
.bq-game-page[data-game="sprint"][data-game-active="true"] .desk-quiz {
  view-transition-name: bq-game-sprint;
}

::view-transition-group(bq-game-sprint) { animation-duration: 380ms; }
</code></pre>
<p>The <code>data-game-active</code> check matters: only a board with a round actually running claims the name, so a setup screen can't steal the morph.</p>
<p>A tile and a board aren't the same shape, so I crop the snapshots instead of letting them squash:</p>
<pre><code class="language-css">::view-transition-old(*), ::view-transition-new(*) {
  height: 100%; object-fit: cover; overflow: clip;
}
</code></pre>
<h2>Decide what stays put</h2>
<p>This was my favourite part. Motion only looks good when something holds still. The header gets its own name and a zero-length animation, so it stays pinned while the page changes under it. The active nav pill gets a name too, and slides to the next link over 300ms:</p>
<pre><code class="language-css">.desk-header { view-transition-name: bq-header; }
::view-transition-group(bq-header) { animation-duration: 0s; }
</code></pre>
<p>The bottom answer sheet gets the same treatment. Without its own name it would shrink into a tile it never belonged to, so it leaves on its own track, sliding down in 160ms:</p>
<pre><code class="language-css">.bq-game-feedback { view-transition-name: bq-sheet; }
::view-transition-old(bq-sheet) { animation: 160ms cubic-bezier(.4, 0, 1, 1) both bq-vt-sheet-out; }
@keyframes bq-vt-sheet-out { to { transform: translateY(100%); } }
</code></pre>
<h2>When the next page is slow</h2>
<p>The browser keeps the old page on screen until the new one is ready, which is lovely when that takes 50ms and awkward when it takes two seconds. So there's a small script on top. After 120ms it shows a progress bar along the top. After 350ms it drops in a skeleton shaped like the destination, and the tile morphs into that skeleton straight away. When the real page arrives, the cross-document transition morphs the skeleton into it. Trimmed down, the swap looks like this:</p>
<pre><code class="language-js">const swap = () =&gt; {
  morphFrom.style.viewTransitionName = 'none';
  skeleton.style.viewTransitionName = `bq-game-${morphFrom.dataset.game}`;
  main.prepend(skeleton);
};

document.startViewTransition(swap).ready.catch(() =&gt; {});
</code></pre>
<p>The tile gives up its name and the skeleton takes it, so the name stays unique. The <code>catch</code> is there on purpose: the browser skips a transition when the next page arrives first, and that's fine.</p>
<h2>The two-pixel bug</h2>
<p>On 12 October the tile-to-board morph was working on desktop and quietly not happening on phones. No error on screen, just a hard cut.</p>
<p>The reason: a view transition gets skipped (the browser throws <code>InvalidStateError</code>) if the area being snapshotted changes between the two pages. On mobile, any horizontal overflow widens the layout viewport. When the board is revealed it slides in with <code>translateX(20px)</code>, and that overflowed the page by 2px. Two pixels were enough for the browser to drop the whole transition.</p>
<p>The fix is one line:</p>
<pre><code class="language-css">main { overflow-x: clip; }
</code></pre>
<p>I used <code>clip</code> rather than <code>hidden</code> on purpose. <code>hidden</code> turns <code>main</code> into a scroll container, which would break the sticky game bar. <code>clip</code> cuts the overflow and leaves everything else alone. I added a frontend test for it, because this is exactly the kind of bug that comes back quietly.</p>
<h2>Small feedback inside a round</h2>
<p>The same "motion shows what changed" idea carries into the game. A new question slides in 20px over 280ms, and the answer sheet slides up over 260ms. Both only run when the visitor hasn't asked for reduced motion:</p>
<pre><code class="language-css">@media (prefers-reduced-motion: no-preference) {
  .desk-quiz-play   { animation: bq-question-in 280ms cubic-bezier(.16, 1, .3, 1) backwards; }
  .bq-game-feedback { animation: bq-sheet-up 260ms cubic-bezier(.16, 1, .3, 1) backwards; }
}
@keyframes bq-question-in { from { opacity: 0; transform: translateX(20px); } }
@keyframes bq-sheet-up    { from { transform: translateY(100%); } }
</code></pre>
<p>I use the same <code>@view-transition</code> rule on <a href="https://aimantas.com/writing/having-fun-with-css-view-transitions/?utm_source=hashnode&amp;utm_medium=community">aimantas.com</a> too, where it's a quiet crossfade, plus a scroll-driven reading bar with <code>animation-timeline: scroll(root)</code> and no scroll listener. The original post there has live demos you can tap.</p>
<h2>If you want to try it</h2>
<ol>
<li>Add <code>@view-transition { navigation: auto; }</code> and switch it off for <code>prefers-reduced-motion</code>. Best effect for the least code.</li>
<li>Give an element on both pages the same unique <code>view-transition-name</code>. In a list, name each item.</li>
<li>Decide what should stay still. A pinned header makes everything else look deliberate.</li>
<li>Make leaving faster than arriving.</li>
<li>Crop snapshots with <code>object-fit</code> when shapes differ, and check your phone for a stray pixel of overflow.</li>
</ol>
<h2>What I haven't checked</h2>
<p>I've read the stylesheet and script and confirmed the same rules are on the live ball-quiz.com stylesheet (2 November 2026). I haven't tested the motion in every browser, and I haven't measured whether it changes how many rounds people play. It feels better to me. Whether it <em>is</em> better is a separate question, and I'd rather find that out than assume it.</p>
<p>If you try this in your own app, I'd like to hear what you ended up naming your first transition. I write up what I'm building, and what breaks, at <a href="https://aimantas.com/writing/?utm_source=hashnode&amp;utm_medium=community">aimantas.com</a>.</p>
]]></content:encoded></item></channel></rss>