DEV Community

Cover image for easy-live2d - Making Live2D integration easier!
老船长PZ_Jack
老船长PZ_Jack

Posted on

easy-live2d - Making Live2D integration easier!

feuse-mcp

easy-live2d

Making Live2D integration easier! A lightweight, developer-friendly Live2D Web SDK wrapper library based on Pixi.js.

Make your Live2D as easy to control as a pixi sprite!

You can directly experience the charm of easy-live2d in your browser using this cloud IDE StackBlitz! 😋

📖 Documentation

👉 easy-live2d Official Documentation


TODO

  • (✅) Transfer Core capabilities to Sprite
  • (✅) Read model paths
  • (✅) Configuration file migration
  • (✅) Direct control of expressions and actions
  • (✅) Expose various event functions
  • (✅) Voice functionality
  • (✅ -) Mouth synchronization - Currently only supports wav format
  • WebGL rendering mounting issues (tentative)

✨ Features

  • ⚡️ Support for Pixi.js v8 and Cubism 5 (both latest versions)
  • 🌟 Ultra-lightweight, removing redundant features
  • 🚀 Simpler API interface
  • 🛠️ Compatible with official Live2D Web SDK
  • 📦 Adaptable to modern frontend frameworks (like Vue, React)

⛵️ Developers

Due to Live2D policy, you need to download Live2D Cubism SDK for Web from the Live2D Cubism official website
and place its Core directory in the /packages/cubism directory


📦 Installation

pnpm add easy-live2d # or npm install easy-live2d # or yarn add easy-live2d 
Enter fullscreen mode Exit fullscreen mode

🛠️ Quick Start

You can also refer to the code in the StackBlitz cloud IDE

Make sure to include Cubism Core in your index.html:
Download it directly from the Live2D Cubism official website: Live2D Cubism SDK for Web

Native HTML

<!doctype html> <html lang=""> <head> <meta charset="UTF-8" /> <link rel="icon" href="/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vite App</title> <style> html, body { overflow: hidden; margin: 0; } </style> </head> <body> <div id="app"></div> <script src="/Core/live2dcubismcore.js"></script> <script type="module"> import { Application, Ticker } from 'pixi.js'; import { Live2DSprite, Config, Priority, LogLevel } from 'easy-live2d'; // Configure basic settings Config.MotionGroupIdle = 'Idle'; // Set default idle motion group Config.MouseFollow = false; // Disable mouse following Config.CubismLoggingLevel = LogLevel.LogLevel_Off // Set logging level // Create Live2D sprite and initialize const live2DSprite = new Live2DSprite(); live2DSprite.init({ modelPath: '/Resources/Huusya/Huusya.model3.json', ticker: Ticker.shared }); // Listen for click events live2DSprite.onLive2D('hit', ({ hitAreaName, x, y }) => { console.log('hit', hitAreaName, x, y); }) // You can also initialize directly like this // const live2DSprite = new Live2DSprite({ // modelPath: '/Resources/Huusya/Huusya.model3.json', // ticker: Ticker.shared // }) // Create application const init = async () => { // You can also initialize directly like this // const model2Json = await (await fetch(path)).json() // const modelSetting = new CubismSetting({ // prefixPath: '/Resources/Hiyori/', // modelJSON: model2Json, // }) // live2DSprite.init({ // modelSetting, // ticker: Ticker.shared, // }) const app = new Application(); await app.init({ view: document.getElementById('live2d'), backgroundAlpha: 0, // Set alpha to 0 for transparency if needed }); // Live2D sprite size and position settings live2DSprite.x = -300 live2DSprite.y = -300 live2DSprite.width = canvasRef.value.clientWidth * window.devicePixelRatio live2DSprite.height = canvasRef.value.clientHeight * window.devicePixelRatio app.stage.addChild(live2dSprite); // Set expression live2DSprite.setExpression({ expressionId: 'normal', }) // Play voice live2DSprite.playVoice({ // Current mouth synchronization only supports wav format voicePath: '/Resources/Huusya/voice/test.wav', }) // Stop voice // live2DSprite.stopVoice() setTimeout(() => { // Play voice live2DSprite.playVoice({ voicePath: '/Resources/Huusya/voice/test.wav', immediate: true // Whether to play immediately: default is true, will stop current playing voice and play new voice immediately }) }, 10000) // Set motion live2DSprite.startMotion({ group: 'test', no: 0, priority: 3, }) } init() </script> </body> </html> 
Enter fullscreen mode Exit fullscreen mode

Vue3 Demo: (Note: make sure to include Cubism Core in your index.html entry file)

<script setup lang="ts"> import { onMounted, onUnmounted, ref } from 'vue' import { Config, Live2DSprite, LogLevel, Priority } from 'easy-live2d' import { Application, Ticker } from 'pixi.js' import { initDevtools } from '@pixi/devtools' const canvasRef = ref<HTMLCanvasElement>() const app = new Application() // Set default Config configuration Config.MotionGroupIdle = 'Idle' // Set default idle motion group Config.MouseFollow = false // Disable mouse following Config.CubismLoggingLevel = LogLevel.LogLevel_Off // Set logging level // Create Live2D sprite and initialize const live2DSprite = new Live2DSprite() live2DSprite.init({ modelPath: '/Resources/Hiyori/Hiyori.model3.json', ticker: Ticker.shared }); // Listen for click events live2DSprite.onLive2D('hit', ({ hitAreaName, x, y }) => { console.log('hit', hitAreaName, x, y); }) // You can also initialize directly like this // const live2DSprite = new Live2DSprite({ // modelPath: '/Resources/Huusya/Huusya.model3.json', // ticker: Ticker.shared // }) onMounted(async () => { // You can also initialize directly like this // const model2Json = await (await fetch(path)).json() // const modelSetting = new CubismSetting({ // prefixPath: '/Resources/Hiyori/', // modelJSON: model2Json, // }) // live2DSprite.init({ // modelSetting, // ticker: Ticker.shared, // }) await app.init({ view: canvasRef.value, backgroundAlpha: 0, // Set alpha to 0 for transparency if needed }) if (canvasRef.value) { // Live2D sprite size and position settings live2DSprite.x = -300 live2DSprite.y = -300 live2DSprite.width = canvasRef.value.clientWidth * window.devicePixelRatio live2DSprite.height = canvasRef.value.clientHeight * window.devicePixelRatio app.stage.addChild(live2DSprite); // Set expression live2DSprite.setExpression({ expressionId: 'normal', }) // Play voice live2DSprite.playVoice({ // Current mouth synchronization only supports wav format voicePath: '/Resources/Huusya/voice/test.wav', }) // Stop voice // live2DSprite.stopVoice() setTimeout(() => { // Play voice live2DSprite.playVoice({ voicePath: '/Resources/Huusya/voice/test.wav', immediate: true // Whether to play immediately: default is true, will stop current playing voice and play new voice immediately }) }, 10000) // Set motion live2DSprite.startMotion({ group: 'test', no: 0, priority: 3, }) } }) onUnmounted(() => { // Release instance live2DSprite.destroy() }) </script> <template> <div class="test"> </div> <canvas ref="canvasRef" id="live2d" /> </template> <style> #live2d { position: absolute; top: 0%; right: 0%; width: 100%; height: 100%; } .test { display: inline-block; position: absolute; width: 100%; height: 70%; background-color: pink; } </style> 
Enter fullscreen mode Exit fullscreen mode

Voice Lip-Sync

Method 1:

Enable lip-sync by setting MouthMovement in the Live2D model editor.

You can refer to the official documentation for this method.

Method 2:
In the model's xx.model3.json file, find the "Groups" section with "Name": "LipSync", and add: "Ids":"ParamMouthOpenY". See example below:

{ "Version": 3, "FileReferences": { "Moc": "xx.moc3", "Textures": [ "xx.2048/texture_00.png" ], "Physics": "xx.physics3.json", "DisplayInfo": "xx.cdi3.json", "Motions": { "test": [], "idle": [] }, "Expressions": [] }, "Groups": [ { "Target": "Parameter", "Name": "EyeBlink", "Ids": [] }, { "Target": "Parameter", "Name": "LipSync", "Ids": [ "ParamMouthOpenY" ] } ], "HitAreas": [] } 
Enter fullscreen mode Exit fullscreen mode

Top comments (0)