<?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: milkc0de</title>
    <description>The latest articles on DEV Community by milkc0de (@milkc0de).</description>
    <link>https://dev.to/milkc0de</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%2F4137388%2F581dcdb2-cc4f-4192-8a63-e70c6b966203.png</url>
      <title>DEV Community: milkc0de</title>
      <link>https://dev.to/milkc0de</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/milkc0de"/>
    <language>en</language>
    <item>
      <title>I built ChibiRigKit: turning a single character image into an animated 2D rig</title>
      <dc:creator>milkc0de</dc:creator>
      <pubDate>Tue, 22 Sep 2026 10:21:27 +0000</pubDate>
      <link>https://dev.to/milkc0de/i-built-chibirigkit-turning-a-single-character-image-into-an-animated-2d-rig-2j36</link>
      <guid>https://dev.to/milkc0de/i-built-chibirigkit-turning-a-single-character-image-into-an-animated-2d-rig-2j36</guid>
      <description>&lt;p&gt;I have been building &lt;strong&gt;ChibiRigKit&lt;/strong&gt;, an open-source tool that turns a single character illustration into an animated 2D rig.&lt;/p&gt;

&lt;p&gt;It started as a very small experiment:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can I make this one illustration move a little more naturally?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It has since grown into a complete pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Single illustration
        ↓
Semi-automatic rigging
        ↓
Browser player
        ↓
Camera / microphone tracking
        ↓
Motion recording and playback
        ↓
OBS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can try the browser playground here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://milkc0de.github.io/ChibiRigKit/" rel="noopener noreferrer"&gt;https://milkc0de.github.io/ChibiRigKit/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Source:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/milkc0de/ChibiRigKit" rel="noopener noreferrer"&gt;https://github.com/milkc0de/ChibiRigKit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article describes the project as of &lt;strong&gt;v1.0.0-beta.7&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What ChibiRigKit does
&lt;/h2&gt;

&lt;p&gt;You can start with a normal PNG illustration.&lt;/p&gt;

&lt;p&gt;ChibiRigKit prepares the pieces required for animation, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;face&lt;/li&gt;
&lt;li&gt;sclera&lt;/li&gt;
&lt;li&gt;iris&lt;/li&gt;
&lt;li&gt;eyelashes&lt;/li&gt;
&lt;li&gt;hair&lt;/li&gt;
&lt;li&gt;clothes&lt;/li&gt;
&lt;li&gt;body&lt;/li&gt;
&lt;li&gt;reconstructed face background&lt;/li&gt;
&lt;li&gt;closed eyes&lt;/li&gt;
&lt;li&gt;closed mouth&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The resulting character can be controlled in a browser.&lt;/p&gt;

&lt;p&gt;The runtime currently supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;blinking&lt;/li&gt;
&lt;li&gt;gaze movement&lt;/li&gt;
&lt;li&gt;yaw, pitch and roll&lt;/li&gt;
&lt;li&gt;hair and clothing motion&lt;/li&gt;
&lt;li&gt;simple long-hair physics&lt;/li&gt;
&lt;li&gt;camera-based face tracking&lt;/li&gt;
&lt;li&gt;microphone-driven lip sync&lt;/li&gt;
&lt;li&gt;motion recording and playback&lt;/li&gt;
&lt;li&gt;custom backgrounds&lt;/li&gt;
&lt;li&gt;WebM recording&lt;/li&gt;
&lt;li&gt;local OBS synchronization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The character can also be exported as a self-contained browser player.&lt;/p&gt;




&lt;h2&gt;
  
  
  The main design decision: AI is not the runtime
&lt;/h2&gt;

&lt;p&gt;One of the most important decisions in ChibiRigKit is that AI is used during &lt;strong&gt;authoring&lt;/strong&gt;, not during &lt;strong&gt;playback&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The architecture roughly looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────┐
│ Authoring                   │
│                             │
│ Codex app-server            │
│ GPT-6 Astra / high          │
│ Python and Node.js tools    │
│ Validation                  │
└──────────────┬──────────────┘
               │
               │ rig data
               ▼
┌─────────────────────────────┐
│ Runtime                     │
│                             │
│ HTML / JavaScript           │
│ Canvas                      │
│ MediaPipe                   │
└─────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once a character has been built, the AI system is no longer required.&lt;/p&gt;

&lt;p&gt;The player is just normal web software.&lt;/p&gt;

&lt;p&gt;I like this separation because the finished character does not depend on model latency, API availability, inference cost, or a permanent Internet connection.&lt;/p&gt;

&lt;p&gt;AI helps create the artifact.&lt;/p&gt;

&lt;p&gt;It is not required to keep the artifact alive.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the model does, and what normal code does
&lt;/h2&gt;

&lt;p&gt;I also did not want the model to perform every operation itself.&lt;/p&gt;

&lt;p&gt;There are two different kinds of work in rigging.&lt;/p&gt;

&lt;p&gt;Some problems are semantic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Is this region hair or skin?
Where does the eye actually end?
Which layer should be drawn in front?
Where should this part rotate from?
Did this mask accidentally include clothing?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are useful tasks for a multimodal model.&lt;/p&gt;

&lt;p&gt;Other operations are deterministic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;image alignment
mask operations
cropping
coordinate transforms
hashing
JSON generation
file validation
browser rendering tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are handled by regular Python and Node.js tools.&lt;/p&gt;

&lt;p&gt;So the model decides &lt;strong&gt;what&lt;/strong&gt; needs to be done, while deterministic tooling performs as much of the actual transformation as possible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rigging is iterative
&lt;/h2&gt;

&lt;p&gt;The automation is not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;image
  ↓
LLM
  ↓
done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead, it behaves more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plan
 ↓
Generate
 ↓
Validate
 ↓
Review
 ↓
Fix
 ↓
Validate again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system produces comparison images and machine-readable reports.&lt;/p&gt;

&lt;p&gt;The model can inspect those results and correct masks or placement.&lt;/p&gt;

&lt;p&gt;More importantly, a model saying “finished” is not considered proof that the rig is valid.&lt;/p&gt;

&lt;p&gt;Artifacts, input hashes, project structure and browser rendering are checked separately.&lt;/p&gt;




&lt;h2&gt;
  
  
  Structured contracts instead of relying on prose
&lt;/h2&gt;

&lt;p&gt;Several parts of the authoring process are represented as JSON contracts.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rig.workflow.json
rig.workflow.schema.json
rig.result.schema.json
work/phase-*.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Model outputs are constrained with structured output schemas.&lt;/p&gt;

&lt;p&gt;A phase can explicitly finish as something such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ready_for_validation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;blocked
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes orchestration much easier than trying to infer application state from free-form model responses.&lt;/p&gt;




&lt;h2&gt;
  
  
  Starting from only one image
&lt;/h2&gt;

&lt;p&gt;A normal character image is enough to begin.&lt;/p&gt;

&lt;p&gt;If required, ChibiRigKit creates reference variations such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;normal
flat
blink
mouth_closed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;flat&lt;/code&gt; is the reconstructed face layer behind movable facial features.&lt;/p&gt;

&lt;p&gt;The original image is preserved.&lt;/p&gt;

&lt;p&gt;Generated references, masks, plans, hashes and manifests are stored separately so that the pipeline can detect when an input or generated artifact has changed.&lt;/p&gt;

&lt;p&gt;Hand-drawn references can also be supplied and take priority over generated ones.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why anime-style segmentation is difficult
&lt;/h2&gt;

&lt;p&gt;Character illustrations are not normal photographs.&lt;/p&gt;

&lt;p&gt;A region cannot always be identified by color or edge information alone.&lt;/p&gt;

&lt;p&gt;For example, the system may need to understand that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;blush belongs to the face, not the eye&lt;/li&gt;
&lt;li&gt;front hair must remain in front of the face&lt;/li&gt;
&lt;li&gt;clothing should not leak into a hair mask&lt;/li&gt;
&lt;li&gt;the shoe on the other side of the character is not part of this leg&lt;/li&gt;
&lt;li&gt;an accessory may cross several semantic regions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are exactly the places where semantic reasoning is useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  A shared runtime for different characters
&lt;/h2&gt;

&lt;p&gt;I wanted character-specific data without character-specific animation code.&lt;/p&gt;

&lt;p&gt;The project therefore uses semantic mesh types such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;blink_eye_radial
bend_vertical
soft_body
soft_strip
face_grid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual character structure lives in &lt;code&gt;rig.project.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The JavaScript runtime remains shared.&lt;/p&gt;

&lt;p&gt;This also means I can improve the runtime and update existing character workspaces without regenerating the entire rig.&lt;/p&gt;




&lt;h2&gt;
  
  
  Nine-direction head poses
&lt;/h2&gt;

&lt;p&gt;A flat illustration looks very flat if you simply rotate the head image.&lt;/p&gt;

&lt;p&gt;ChibiRigKit instead stores nine reference configurations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;up-left       up       up-right

left        center       right

down-left    down     down-right
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The runtime interpolates between them.&lt;/p&gt;

&lt;p&gt;Roll is then applied on top of the interpolated yaw/pitch result.&lt;/p&gt;

&lt;p&gt;The poses can affect more than the face:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;eyes&lt;/li&gt;
&lt;li&gt;hair&lt;/li&gt;
&lt;li&gt;face contour&lt;/li&gt;
&lt;li&gt;accessories&lt;/li&gt;
&lt;li&gt;neck connection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not a 360-degree 3D reconstruction.&lt;/p&gt;

&lt;p&gt;It is a 2D deformation system designed for movement around the front-facing pose.&lt;/p&gt;

&lt;p&gt;The nine poses can be edited manually in the browser.&lt;/p&gt;




&lt;h2&gt;
  
  
  Blinking
&lt;/h2&gt;

&lt;p&gt;The eye system can separate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sclera
iris
eyelashes
closed-eye artwork
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The iris moves inside a clipped sclera region.&lt;/p&gt;

&lt;p&gt;During a blink, the open-eye mesh deforms toward the closed-eye line.&lt;/p&gt;

&lt;p&gt;The dedicated closed-eye artwork appears only near full closure.&lt;/p&gt;

&lt;p&gt;This gives a more continuous result than simply switching between two images.&lt;/p&gt;




&lt;h2&gt;
  
  
  Camera tracking
&lt;/h2&gt;

&lt;p&gt;The browser player uses MediaPipe locally.&lt;/p&gt;

&lt;p&gt;Face tracking runs in a Worker and produces values for things such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yaw
pitch
roll
eyes
gaze
mouth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Head, eye and mouth tracking can be enabled or disabled independently.&lt;/p&gt;

&lt;p&gt;That means the input sources can be mixed.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;head  → camera
eyes  → camera
mouth → microphone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No Codex connection is required for tracking.&lt;/p&gt;




&lt;h2&gt;
  
  
  Audio-driven lip sync
&lt;/h2&gt;

&lt;p&gt;Microphone input can directly drive the mouth.&lt;/p&gt;

&lt;p&gt;The player includes an input level meter and sensitivity controls.&lt;/p&gt;

&lt;p&gt;It can also work with virtual audio devices such as VB-CABLE, allowing audio from another application to drive the character.&lt;/p&gt;

&lt;p&gt;The player disables browser echo cancellation, noise suppression and automatic gain control for this input path.&lt;/p&gt;

&lt;p&gt;Stereo channels are inspected separately as well, which avoids some phase-cancellation problems seen with virtual audio routing.&lt;/p&gt;




&lt;h2&gt;
  
  
  ChibiRigMotion
&lt;/h2&gt;

&lt;p&gt;I created a small motion format called &lt;strong&gt;ChibiRigMotion&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of recording camera video or microphone audio, it stores control values over time.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;time
yaw
pitch
roll
eyes
gaze
mouth
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A motion can later be loaded and replayed on the character.&lt;/p&gt;

&lt;p&gt;Tracking and motion playback can also be combined.&lt;/p&gt;

&lt;p&gt;Because the file contains motion values rather than raw media, the recorded motion stays independent from the original camera and microphone streams.&lt;/p&gt;




&lt;h2&gt;
  
  
  Local OBS synchronization
&lt;/h2&gt;

&lt;p&gt;This became one of my favorite additions.&lt;/p&gt;

&lt;p&gt;An exported character package contains launchers for Windows, macOS and Linux.&lt;/p&gt;

&lt;p&gt;The local controller is available at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://127.0.0.1:5510/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The OBS browser source uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://127.0.0.1:5510/obs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting part is that ChibiRigKit does &lt;strong&gt;not&lt;/strong&gt; stream rendered video into OBS.&lt;/p&gt;

&lt;p&gt;Instead, it shares animation state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Controller
    │
    │ settings + motion values
    ▼
Local server
    │
    │ Server-Sent Events
    ▼
OBS browser source
    │
    ▼
Render character locally
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both browser pages run the same character runtime.&lt;/p&gt;

&lt;p&gt;Only the state needs to move between them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why SSE?
&lt;/h2&gt;

&lt;p&gt;The synchronization pattern is primarily:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;controller → server → renderer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The OBS renderer does not need a large bidirectional RPC protocol.&lt;/p&gt;

&lt;p&gt;Server-Sent Events were therefore sufficient and kept the implementation small.&lt;/p&gt;

&lt;p&gt;The server binds only to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is not exposed to the LAN.&lt;/p&gt;




&lt;h2&gt;
  
  
  Raw camera and microphone data are not sent to OBS
&lt;/h2&gt;

&lt;p&gt;For camera tracking, the flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Camera
  ↓
Browser
  ↓
MediaPipe Worker
  ↓
Motion values
  ↓
Local SSE
  ↓
OBS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The OBS synchronization server receives processed values, not the raw camera video.&lt;/p&gt;

&lt;p&gt;The same principle applies to microphone input.&lt;/p&gt;

&lt;p&gt;The exported character does not persist device IDs or device names either.&lt;/p&gt;

&lt;p&gt;ChibiRigKit also does not start OBS recording or streaming by itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Offline playback
&lt;/h2&gt;

&lt;p&gt;Once the required files are installed, playback does not require Internet access.&lt;/p&gt;

&lt;p&gt;The finished character can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;animate&lt;/li&gt;
&lt;li&gt;load motions&lt;/li&gt;
&lt;li&gt;save WebM&lt;/li&gt;
&lt;li&gt;use the local player&lt;/li&gt;
&lt;li&gt;synchronize with OBS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;offline.&lt;/p&gt;

&lt;p&gt;If the MediaPipe SDK, WASM and face model have already been prepared, camera and microphone tracking can also run offline.&lt;/p&gt;

&lt;p&gt;Internet access is mainly needed during initial setup and AI-assisted rig creation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Exporting a character
&lt;/h2&gt;

&lt;p&gt;A character can be exported with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--character&lt;/span&gt; characters/&amp;lt;name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dist/&amp;lt;name&amp;gt;/
dist/&amp;lt;name&amp;gt;.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated HTML contains the character artwork and configuration.&lt;/p&gt;

&lt;p&gt;The ZIP also contains the small local server, platform launchers, tracking runtime and required license notices.&lt;/p&gt;

&lt;p&gt;Authoring files such as masks, model work files and intermediate validation data stay in the development workspace.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick start
&lt;/h2&gt;

&lt;p&gt;Setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sh SETUP.command
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a rig:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sh AUTO_RIG.command &lt;span class="s2"&gt;"/path/to/character.png"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open a character in the player:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run player:setup
npm run player &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--character&lt;/span&gt; characters/&amp;lt;name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Export:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--character&lt;/span&gt; characters/&amp;lt;name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Licensing
&lt;/h2&gt;

&lt;p&gt;The current ChibiRigKit source code is released under the &lt;strong&gt;Apache License 2.0&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Versions that were previously released under the MIT License remain available under those original MIT terms.&lt;/p&gt;

&lt;p&gt;Media is treated separately from source code.&lt;/p&gt;

&lt;p&gt;Character artwork, screenshots, videos, logos and similar assets are not automatically covered by the source-code license.&lt;/p&gt;

&lt;p&gt;Third-party components such as MediaPipe retain their own license and notice files, including inside exported packages where required.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I learned from building it
&lt;/h2&gt;

&lt;p&gt;The most interesting part of this project has not really been “using AI to animate an image.”&lt;/p&gt;

&lt;p&gt;It has been deciding &lt;strong&gt;where AI should stop&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The architecture I ended up with is roughly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI
  → semantic decisions

Python / Node.js
  → deterministic transformations and validation

JSON
  → contracts between the two

Browser
  → runtime

Local SSE
  → OBS synchronization
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That boundary gives me the benefits of multimodal reasoning during authoring without making the final character dependent on an AI service.&lt;/p&gt;

&lt;p&gt;ChibiRigKit is still in beta and I keep changing it as I use it, but it has already grown far beyond the tiny experiment it started as.&lt;/p&gt;

&lt;p&gt;One image was enough to begin.&lt;/p&gt;

&lt;p&gt;Now the same character can blink, look around, move its head, react to a camera and microphone, replay recorded motions, and appear directly inside OBS.&lt;/p&gt;

&lt;p&gt;And the finished character still runs as normal browser software. 🎀&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
