<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: AniMate Waifu</title>
    <description>The latest articles on DEV Community by AniMate Waifu (@animate).</description>
    <link>https://dev.to/animate</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4076602%2F5312aedf-7b23-4294-9ced-b8cea7f07590.png</url>
      <title>DEV Community: AniMate Waifu</title>
      <link>https://dev.to/animate</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/animate"/>
    <language>en</language>
    <item>
      <title>Keeping a 3D overlay light enough to leave running all day</title>
      <dc:creator>AniMate Waifu</dc:creator>
      <pubDate>Mon, 28 Sep 2026 14:49:27 +0000</pubDate>
      <link>https://dev.to/animate/keeping-a-3d-overlay-light-enough-to-leave-running-all-day-5ejo</link>
      <guid>https://dev.to/animate/keeping-a-3d-overlay-light-enough-to-leave-running-all-day-5ejo</guid>
      <description>&lt;p&gt;This is the third post in a series about building AniMate Waifu, a desktop companion that puts a VRM character on top of your desktop. Post one covered the three problems that fought back the hardest; post two was about why an imported model usually isn't the thing that breaks. This one is about the constraint that shaped most of the rendering code: an overlay that lives on someone's desktop all day has to be cheap enough that they never have to think about it.&lt;/p&gt;

&lt;p&gt;A desktop pet is not a game. A game gets your full attention for an hour; a desktop companion shares your screen with everything else you do, for as long as you keep it open. The character sits in a corner while you read, write, or wait for a build. That means idle is not an edge case — idle is the default state, and idle is what has to be cheap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cap the loop first
&lt;/h2&gt;

&lt;p&gt;The simplest decision had the biggest effect: the render loop doesn't run uncapped. The default target is 30 fps, with 15 / 30 / 60 selectable. Nothing about a character that occupies a corner of your screen needs 120 fps — above roughly 30, the extra frames are electricity, not smoothness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Idle should cost less than active
&lt;/h2&gt;

&lt;p&gt;On top of the cap, the app watches for interaction. After 30 seconds with no activity it drops to 15 fps; the next interaction restores the normal rate immediately. The exact numbers matter less than the signal: "the user hasn't touched it in a while" is information you already have for free, and acting on it is the difference between an overlay people keep running and one they quit.&lt;/p&gt;

&lt;h2&gt;
  
  
  One pacing bug worth passing on
&lt;/h2&gt;

&lt;p&gt;The throttle itself had a subtle failure. requestAnimationFrame fires at your display's refresh rate, and the naive pattern — skip the frame if not enough time has elapsed, otherwise set lastTime = now — drifts on displays that don't divide cleanly. On a 59.94 Hz panel the fractional remainder accumulates until the throttle starts dropping every other render, and the character stutters for no visible reason. The fix is one line: carry the remainder forward (lastTime = lastTime + interval) instead of resetting it to now. Small, but it's exactly the kind of thing that shows up as "feels janky on some machines" and nowhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Throttling is contextual, not global
&lt;/h2&gt;

&lt;p&gt;The idle rule applies to the overlay — not to every surface in the app. The Workshop, where you preview characters, dances and stages, opts out of the throttle entirely while it is on screen, because judging a dance at 15 fps is useless. One shared frame controller, different policies per surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify it on your own machine
&lt;/h2&gt;

&lt;p&gt;If you run any desktop pet — ours or someone else's — open Task Manager or Activity Monitor, leave it alone for half a minute, and watch: idle usage should be visibly lower than active usage. If a pet burns the same CPU doing nothing as it does dancing, it is rendering every frame for no reason.&lt;/p&gt;

&lt;p&gt;And if ours is the one misbehaving on your machine, the &lt;a href="https://getanimate.app/desktop-pet-performance?utm_source=devto&amp;amp;utm_medium=column&amp;amp;utm_campaign=backlink_cold_start&amp;amp;utm_content=devto_perf_003" rel="noopener noreferrer"&gt;four steps to find the real fix&lt;/a&gt; on our site are the user-facing version of this post — no code required.&lt;/p&gt;

&lt;p&gt;Issues and releases live on &lt;a href="https://github.com/channingxiao18/AniMate-Waifu" rel="noopener noreferrer"&gt;the GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next in the series
&lt;/h2&gt;

&lt;p&gt;One promised topic left: the licensing side — which VRM models you can and can't show or ship, and why "it was on the internet" isn't a licence. If you've tuned an always-on app for power, I'd like to hear what worked.&lt;/p&gt;

</description>
      <category>vrm</category>
      <category>ai</category>
      <category>performance</category>
    </item>
    <item>
      <title>It's usually not the model: what actually breaks when users import their own VRM avatars</title>
      <dc:creator>AniMate Waifu</dc:creator>
      <pubDate>Mon, 21 Sep 2026 06:59:58 +0000</pubDate>
      <link>https://dev.to/animate/its-usually-not-the-model-what-actually-breaks-when-users-import-their-own-vrm-avatars-5f0b</link>
      <guid>https://dev.to/animate/its-usually-not-the-model-what-actually-breaks-when-users-import-their-own-vrm-avatars-5f0b</guid>
      <description>&lt;p&gt;Last week I posted about &lt;a href="https://getanimate.app/?utm_source=devto&amp;amp;utm_medium=column&amp;amp;utm_campaign=backlink_cold_start&amp;amp;utm_content=devto_intro_001" rel="noopener noreferrer"&gt;AniMate Waifu&lt;/a&gt; — a desktop companion that puts a VRM character on your desktop — and the three things that fought back while building it. This is the follow-up I promised: what actually breaks when users import their own models.&lt;/p&gt;

&lt;p&gt;Short version: it's usually not the model.&lt;/p&gt;

&lt;p&gt;Once your app accepts user-supplied files, you inherit every way a file can be wrong. Ours imports VRM 0.x and VRM 1.0 — files arrive from VRoid Studio, from Blender, and from conversion pipelines out of PMX and FBX. After enough "my model is broken" reports, a pattern shows up fast, and the pattern is not broken geometry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four checks before blaming the model
&lt;/h2&gt;

&lt;p&gt;None of these involve the file, which is exactly why they're worth running first:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Is the app current?&lt;/strong&gt; Format support widens with releases. An import that fails on an old build may be a bug that's already fixed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is the feature unlocked?&lt;/strong&gt; In our app, custom import is the paid feature. Better to surface that before a user spends an evening debugging their model against a paywall.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Did the download complete?&lt;/strong&gt; A truncated &lt;code&gt;.vrm&lt;/code&gt; is the single most common "corrupt file" report — and it's a download problem, not a model problem. Compare the file size against the source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is it actually a &lt;code&gt;.vrm&lt;/code&gt; file?&lt;/strong&gt; Not &lt;code&gt;.pmx&lt;/code&gt;, not &lt;code&gt;.fbx&lt;/code&gt;, not a Live2D &lt;code&gt;.model3.json&lt;/code&gt;, not a folder of textures. One file, ending in &lt;code&gt;.vrm&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The rename trap
&lt;/h2&gt;

&lt;p&gt;VRM is a profile on top of glTF 2.0, and renaming doesn't convert anything. A &lt;code&gt;.pmx&lt;/code&gt; or &lt;code&gt;.fbx&lt;/code&gt; renamed to &lt;code&gt;.vrm&lt;/code&gt; still has the wrong bones and shaders inside. Plain glTF out of Blender renamed to &lt;code&gt;.vrm&lt;/code&gt; is the same story — the extension alone doesn't make a file VRM, which is why the Blender path goes through the VRM add-on: it maps the humanoid bones and writes the VRM extension block.&lt;/p&gt;

&lt;p&gt;Thirty-second check: rename a real &lt;code&gt;.vrm&lt;/code&gt; to &lt;code&gt;.glb&lt;/code&gt; and a glTF viewer will parse it (materials will look wrong — that's MToon, and that's fine). Rename a &lt;code&gt;.pmx&lt;/code&gt; to &lt;code&gt;.vrm&lt;/code&gt; and nothing will parse it at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the symptoms are actually telling you
&lt;/h2&gt;

&lt;p&gt;When the file genuinely is a VRM and it still misbehaves, the symptom usually names the cause:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Usual cause&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Imports, then stands frozen&lt;/td&gt;
&lt;td&gt;Humanoid bone mapping missing or non-standard — the mesh loads, the rig doesn't&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Black, glowing, flat or invisible materials&lt;/td&gt;
&lt;td&gt;Version mismatch: 0.x uses MToon, 1.0 uses MToon10 — a file exported as one version with materials authored for the other&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No facial expressions&lt;/td&gt;
&lt;td&gt;Blendshapes missing, or a 0.x/1.0 naming difference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hair and skirt rigid&lt;/td&gt;
&lt;td&gt;SpringBone physics stripped on export&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Very slow&lt;/td&gt;
&lt;td&gt;Polygon, material and physics weight — an overlay that renders all day punishes what a game engine forgives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wrong size or facing the wrong way&lt;/td&gt;
&lt;td&gt;Not a defect: models arrive at wildly different scales, so scale is the first knob, not a bug report&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The second row is the sneaky one: the file is internally consistent — it was just exported as one version with materials authored for the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Export settings beat modelling
&lt;/h2&gt;

&lt;p&gt;If there's one takeaway, it's this: export configuration causes more import failures than the modelling does.&lt;/p&gt;

&lt;p&gt;Exporting from VRoid Studio: confirm the humanoid rig, include expressions, keep SpringBone physics — and let VRoid reduce polygon and material counts on export, because at desk scale nobody sees the difference and the GPU notices. One more version note: current VRoid Studio dress-up exports are VRM 1.0 only, so this trap is getting more common, not less.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we changed app-side
&lt;/h2&gt;

&lt;p&gt;Two things, after enough of these reports. Parse at preview, before anything is committed — a bad file surfaces there with its reason, not after it's already in the library. And never open with "your file is broken": a meaningful share of those reports turn out to be something on our side. We keep the current version of the checklist in &lt;a href="https://getanimate.app/guides/import-vrm-model?utm_source=devto&amp;amp;utm_medium=column&amp;amp;utm_campaign=backlink_cold_start&amp;amp;utm_content=devto_import_002" rel="noopener noreferrer"&gt;our VRM import guide&lt;/a&gt;, and issues and releases live in &lt;a href="https://github.com/channingxiao18/AniMate-Waifu" rel="noopener noreferrer"&gt;the GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next
&lt;/h2&gt;

&lt;p&gt;From last post's list, two candidates remain: the licensing side — which VRM models you can actually show and ship — and keeping a 3D overlay light enough to leave running all day.&lt;/p&gt;

&lt;p&gt;If you've shipped an import pipeline, 3D or otherwise: what's the failure mode your users keep finding?&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>vrm</category>
      <category>ai</category>
      <category>threejs</category>
    </item>
    <item>
      <title>Show DEV: a VRM desktop companion built with Tauri + Three.js — and the three things that fought back</title>
      <dc:creator>AniMate Waifu</dc:creator>
      <pubDate>Tue, 15 Sep 2026 06:25:06 +0000</pubDate>
      <link>https://dev.to/animate/show-dev-a-vrm-desktop-companion-built-with-tauri-threejs-and-the-three-things-that-fought-ba4</link>
      <guid>https://dev.to/animate/show-dev-a-vrm-desktop-companion-built-with-tauri-threejs-and-the-three-things-that-fought-ba4</guid>
      <description>&lt;p&gt;I've been building &lt;strong&gt;&lt;a href="https://getanimate.app/?utm_source=devto&amp;amp;utm_medium=column&amp;amp;utm_campaign=backlink_cold_start&amp;amp;utm_content=devto_intro_001" rel="noopener noreferrer"&gt;AniMate Waifu&lt;/a&gt;&lt;/strong&gt; — a desktop companion app that puts a 3D anime character on top of your desktop. It runs on Windows and macOS, and it's the kind of software that has to survive being left running while you do something else.&lt;/p&gt;

&lt;p&gt;This is the intro post: what it does today, and the three problems that took the most attempts. I'll keep posting the ones I hit from here.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is
&lt;/h2&gt;

&lt;p&gt;AniMate Waifu puts a character on your desktop as a &lt;strong&gt;transparent, always-on-top overlay&lt;/strong&gt;. Not a wallpaper, not a browser tab — it sits in front of your windows while you work, and it can be set click-through so it never eats your clicks.&lt;/p&gt;

&lt;p&gt;You bring the character: it imports &lt;strong&gt;VRM 0.x and VRM 1.0&lt;/strong&gt; files, including what VRoid Studio exports. On the desktop it tracks your cursor, reacts when you touch it, can be dragged and resized, has idle motion, and moves to whatever you're playing. &lt;strong&gt;VRMA&lt;/strong&gt; dance files can be imported once and applied to any character in your library. A Workshop keeps characters, motions, dances and stages together with 3D previews. Text and voice conversation runs on a persona and TTS voice you set per character; chat history stays on your device, and chat needs a connection.&lt;/p&gt;

&lt;p&gt;Free tier ships with built-in characters. A one-time $9.90 unlock covers your own avatars, dances, motions and stages.&lt;/p&gt;

&lt;p&gt;Stack, since this is dev.to: &lt;strong&gt;Tauri + Three.js + @pixiv/three-vrm&lt;/strong&gt;. The app is closed source; the GitHub repo is where issues and releases live.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three things that fought back
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Transparent, always-on-top and click-through are three requirements that fight each other.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Keeping the character above your windows was the easy part. Keeping it there &lt;em&gt;without&lt;/em&gt; a title bar, &lt;em&gt;without&lt;/em&gt; stealing focus, and &lt;em&gt;while&lt;/em&gt; letting clicks fall through to whatever is underneath took more iterations than the rendering did. Click-through is the awkward one: the window still has to render and animate, but must opt out of hit-testing — and that's platform-specific behaviour, not a flag you set once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. VRM 0.x and 1.0 are not the same file with a bumped version number.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;0.x uses MToon; 1.0 uses MToon10 and adds capsule colliders, per-joint SpringBone tuning and MatCap masking. If you have a &lt;code&gt;.vrm&lt;/code&gt; and don't know which you're holding: open it in a text editor and search for &lt;code&gt;VRMC_vrm&lt;/code&gt;. Present → 1.0. Only &lt;code&gt;VRM&lt;/code&gt; → 0.x. Renaming doesn't convert anything, because the bone and shader definitions differ.&lt;/p&gt;

&lt;p&gt;The practical consequence: a model a user downloads may simply not load, and the error you get rarely tells you why. PMX and FBX have to be converted to VRM first, which is a detour of its own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. "Lightweight" is a design constraint, not a feature you add later.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An overlay that is always on screen can't be the thing that heats up the laptop. That shaped decisions early: what runs per frame, what gets cached, what stays asleep until something actually happens. It's also why the macOS build targets Apple Silicon only — shipping a second, slower path for Intel Macs would have meant maintaining two performance profiles for a shrinking install base.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'll write about next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What actually breaks when users import their own models (it's usually not the model)&lt;/li&gt;
&lt;li&gt;Keeping a 3D overlay light enough to leave running all day&lt;/li&gt;
&lt;li&gt;The licensing side: which VRM models you can and can't show or ship&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've built something that has to live on someone else's desktop — especially anything with overlay windows — I'd like to hear what bit you.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>vrm</category>
      <category>ai</category>
      <category>threejs</category>
    </item>
  </channel>
</rss>
