diff -pruN --exclude build ./demo_clean/CMakeLists.txt ./demo_dev/CMakeLists.txt
--- ./demo_clean/CMakeLists.txt	2025-05-30 00:59:58.252401066 +0100
+++ ./demo_dev/CMakeLists.txt	2025-05-30 01:05:35.538986524 +0100
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.16)
 # Set app name.
 set(APP_NAME Demo)
 # Set directory paths.
-set(SDK_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../../..)
+set(SDK_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../CubismSdkForNative-5-r.4.1)
 set(CORE_PATH ${SDK_ROOT_PATH}/Core)
 set(FRAMEWORK_PATH ${SDK_ROOT_PATH}/Framework)
 set(THIRD_PARTY_PATH ${SDK_ROOT_PATH}/Samples/OpenGL/thirdParty)
@@ -35,7 +35,7 @@ set(GLFW_INSTALL OFF CACHE BOOL "" FORCE
 set(BUILD_UTILS OFF CACHE BOOL "" FORCE)
 
 # Specify version of compiler.
-set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 set(CMAKE_CXX_EXTENSIONS OFF)
 
@@ -67,6 +67,11 @@ target_link_libraries(Framework Live2DCu
 # Find opengl libraries.
 find_package(OpenGL REQUIRED)
 
+# Add MouseTrackerForCubism
+find_package(PkgConfig)
+pkg_check_modules(GTKMM gtkmm-3.0)
+add_subdirectory(../.. MouseTrackerForCubism_build)
+
 # Make executable app.
 add_executable(${APP_NAME})
 # Add common source files.
@@ -79,9 +84,20 @@ target_link_libraries(${APP_NAME}
   Framework
   glfw
   ${OPENGL_LIBRARIES}
+  MouseTrackerForCubism
+  stdc++fs
 )
 # Specify include directories.
-target_include_directories(${APP_NAME} PRIVATE ${STB_PATH})
+target_include_directories(${APP_NAME} PRIVATE ${STB_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../include ${GTKMM_INCLUDE_DIRS})
+
+# Copy GUI to build directory
+add_custom_command(
+  TARGET ${APP_NAME}
+  POST_BUILD
+  COMMAND
+    ${CMAKE_COMMAND} -E
+      copy ${CMAKE_CURRENT_SOURCE_DIR}/../../src/gui.glade $<TARGET_FILE_DIR:${APP_NAME}>/gui.glade
+)
 
 # Copy resource directory to build directory.
 add_custom_command(
diff -pruN --exclude build ./demo_clean/scripts/make_gcc ./demo_dev/scripts/make_gcc
--- ./demo_clean/scripts/make_gcc	2025-05-30 00:59:58.252401066 +0100
+++ ./demo_dev/scripts/make_gcc	2023-05-28 09:11:29.467788463 +0100
@@ -5,42 +5,9 @@ set -ue
 SCRIPT_PATH=$(cd $(dirname $0) && pwd)
 CMAKE_PATH=$SCRIPT_PATH/..
 BUILD_PATH=$SCRIPT_PATH/../build/make_gcc
-MINIMUM_DEMO="OFF"
-DATA=""
-
-if [ "$#" -ne 0 ]; then
- DATA="$1"
-fi
-
-while :
-do
-
- if [ -z "$DATA" ]; then
-   echo "Choose which format you would like to create the demo."
-   echo "Full version : 1"
-   echo "Minimum version : 2"
-   read -p "Your Choice : " DATA
- fi
-
- case "$DATA" in
-   "1" )
-     echo "Making Full Demo"
-     MINIMUM_DEMO="OFF"
-     break ;;
-   "2" )
-     echo "Making Minimum Demo"
-     MINIMUM_DEMO="ON"
-     break ;;
-   * )
-     echo "You need to enter a valid number."
-     DATA="" ;;
- esac
-done
 
 # Run CMake.
 cmake -S "$CMAKE_PATH" \
   -B "$BUILD_PATH" \
-  -D CMAKE_BUILD_TYPE=Release \
-  -D CSM_MINIMUM_DEMO=$MINIMUM_DEMO \
-  -D GLFW_BUILD_WAYLAND=OFF
-cd "$BUILD_PATH" && make
+  -D CMAKE_BUILD_TYPE=Release
+cd "$BUILD_PATH" && make -j4
diff -pruN --exclude build ./demo_clean/src/LAppDefine.cpp ./demo_dev/src/LAppDefine.cpp
--- ./demo_clean/src/LAppDefine.cpp	2025-05-30 00:59:58.252401066 +0100
+++ ./demo_dev/src/LAppDefine.cpp	2025-05-30 01:07:49.665920483 +0100
@@ -60,11 +60,11 @@ namespace LAppDefine {
     const csmInt32 PriorityForce = 3;
 
     // デバッグ用ログの表示オプション
-    const csmBool DebugLogEnable = true;
+    const csmBool DebugLogEnable = false;
     const csmBool DebugTouchLogEnable = false;
 
     // Frameworkから出力するログのレベル設定
-    const CubismFramework::Option::LogLevel CubismLoggingLevel = CubismFramework::Option::LogLevel_Verbose;
+    const CubismFramework::Option::LogLevel CubismLoggingLevel = CubismFramework::Option::LogLevel_Warning;
 
     // デフォルトのレンダーターゲットサイズ
     const csmInt32 RenderTargetWidth = 1900;
diff -pruN --exclude build ./demo_clean/src/LAppDelegate.cpp ./demo_dev/src/LAppDelegate.cpp
--- ./demo_clean/src/LAppDelegate.cpp	2025-05-30 00:59:58.252401066 +0100
+++ ./demo_dev/src/LAppDelegate.cpp	2025-05-30 01:09:31.843395630 +0100
@@ -46,7 +46,8 @@ void LAppDelegate::ReleaseInstance()
     s_instance = NULL;
 }
 
-bool LAppDelegate::Initialize()
+bool LAppDelegate::Initialize(int initWindowWidth, int initWindowHeight,
+                              const char *windowTitle)
 {
     if (DebugLogEnable)
     {
@@ -64,7 +65,13 @@ bool LAppDelegate::Initialize()
     }
 
     // Windowの生成_
-    _window = glfwCreateWindow(RenderTargetWidth, RenderTargetHeight, "SAMPLE", NULL, NULL);
+    _window = glfwCreateWindow(
+        initWindowWidth ? initWindowWidth : RenderTargetWidth,
+        initWindowHeight ? initWindowHeight : RenderTargetHeight,
+        windowTitle ? windowTitle : "SAMPLE",
+        NULL,
+        NULL);
+
     if (_window == NULL)
     {
         if (DebugLogEnable)
@@ -96,10 +103,6 @@ bool LAppDelegate::Initialize()
     glEnable(GL_BLEND);
     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
 
-    //コールバック関数の登録
-    glfwSetMouseButtonCallback(_window, EventHandler::OnMouseCallBack);
-    glfwSetCursorPosCallback(_window, EventHandler::OnMouseCallBack);
-
     // ウィンドウサイズ記憶
     int width, height;
     glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &width, &height);
diff -pruN --exclude build ./demo_clean/src/LAppDelegate.hpp ./demo_dev/src/LAppDelegate.hpp
--- ./demo_clean/src/LAppDelegate.hpp	2025-05-30 00:59:58.252401066 +0100
+++ ./demo_dev/src/LAppDelegate.hpp	2025-05-30 01:10:00.192935412 +0100
@@ -40,7 +40,8 @@ public:
     /**
     * @brief   APPに必要なものを初期化する。
     */
-    bool Initialize();
+    bool Initialize(int initWindowWidth = 0, int initWindowHeight = 0,
+                    const char *windowTitle = "SAMPLE");
 
     /**
     * @brief   解放する。
diff -pruN --exclude build ./demo_clean/src/LAppLive2DManager.cpp ./demo_dev/src/LAppLive2DManager.cpp
--- ./demo_clean/src/LAppLive2DManager.cpp	2025-05-30 00:59:58.252401066 +0100
+++ ./demo_dev/src/LAppLive2DManager.cpp	2025-05-30 01:12:18.865322293 +0100
@@ -6,13 +6,7 @@
  */
 
 #include "LAppLive2DManager.hpp"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <dirent.h>
-#include <unistd.h>
-#include <libgen.h>
-#include <limits.h>
+#include <string>
 #include <GL/glew.h>
 #include <GLFW/glfw3.h>
 #include <Rendering/CubismRenderer.hpp>
@@ -68,12 +62,11 @@ void LAppLive2DManager::ReleaseInstance(
 
 LAppLive2DManager::LAppLive2DManager()
     : _viewMatrix(NULL)
-    , _sceneIndex(0)
+    , _projScaleFactor(1.0f)
+    , _translateX(0.0f)
+    , _translateY(0.0f)
 {
     _viewMatrix = new CubismMatrix44();
-    SetUpModel();
-
-    ChangeScene(_sceneIndex);
 }
 
 LAppLive2DManager::~LAppLive2DManager()
@@ -92,60 +85,6 @@ void LAppLive2DManager::ReleaseAllModel(
     _models.Clear();
 }
 
-void LAppLive2DManager::SetUpModel()
-{
-    // ResourcesPathの中にあるフォルダ名を全てクロールし、モデルが存在するフォルダを定義する。
-    // フォルダはあるが同名の.model3.jsonが見つからなかった場合はリストに含めない。
-    struct dirent *dirent;
-    csmString crawlPath(LAppDelegate::GetInstance()->GetExecuteAbsolutePath().c_str());
-    crawlPath += ResourcesPath;
-
-    DIR *pDir = opendir(crawlPath.GetRawString());
-    if (pDir == NULL) return;
-
-    _modelDir.Clear();
-
-    while ((dirent = readdir(pDir)) != NULL)
-    {
-        if ((dirent->d_type & DT_DIR) && strcmp(dirent->d_name, "..") != 0)
-        {
-            // フォルダと同名の.model3.jsonがあるか探索する
-            struct dirent *dirent2;
-
-            csmString modelName(dirent->d_name);
-
-            csmString modelPath(crawlPath);
-            modelPath += modelName;
-            modelPath.Append(1, '/');
-
-            csmString model3jsonName(modelName);
-            model3jsonName += ".model3.json";
-
-            DIR *pDir2 = opendir(modelPath.GetRawString());
-            while ((dirent2 = readdir(pDir2)) != NULL)
-            {
-                if (strcmp(dirent2->d_name, model3jsonName.GetRawString()) == 0)
-                {
-                    _modelDir.PushBack(csmString(dirent->d_name));
-                }
-            }
-            closedir(pDir2);
-        }
-    }
-    closedir(pDir);
-    qsort(_modelDir.GetPtr(), _modelDir.GetSize(), sizeof(csmString), CompareCsmString);
-}
-
-csmVector<csmString> LAppLive2DManager::GetModelDir() const
-{
-    return _modelDir;
-}
-
-csmInt32 LAppLive2DManager::GetModelDirSize() const
-{
-    return _modelDir.GetSize();
-}
-
 LAppModel* LAppLive2DManager::GetModel(csmUint32 no) const
 {
     if (no < _models.GetSize())
@@ -172,26 +111,6 @@ void LAppLive2DManager::OnTap(csmFloat32
     {
         LAppPal::PrintLogLn("[APP]tap point: {x:%.2f y:%.2f}", x, y);
     }
-
-    for (csmUint32 i = 0; i < _models.GetSize(); i++)
-    {
-        if (_models[i]->HitTest(HitAreaNameHead, x, y))
-        {
-            if (DebugLogEnable)
-            {
-                LAppPal::PrintLogLn("[APP]hit area: [%s]", HitAreaNameHead);
-            }
-            _models[i]->SetRandomExpression();
-        }
-        else if (_models[i]->HitTest(HitAreaNameBody, x, y))
-        {
-            if (DebugLogEnable)
-            {
-                LAppPal::PrintLogLn("[APP]hit area: [%s]", HitAreaNameBody);
-            }
-            _models[i]->StartRandomMotion(MotionGroupTapBody, PriorityNormal, FinishedMotion, BeganMotion);
-        }
-    }
 }
 
 void LAppLive2DManager::OnUpdate() const
@@ -215,12 +134,15 @@ void LAppLive2DManager::OnUpdate() const
         {
             // 横に長いモデルを縦長ウィンドウに表示する際モデルの横サイズでscaleを算出する
             model->GetModelMatrix()->SetWidth(2.0f);
-            projection.Scale(1.0f, static_cast<float>(width) / static_cast<float>(height));
+            projection.Scale(_projScaleFactor,
+                             _projScaleFactor * static_cast<float>(width) / static_cast<float>(height));
         }
         else
         {
-            projection.Scale(static_cast<float>(height) / static_cast<float>(width), 1.0f);
+            projection.Scale(_projScaleFactor * static_cast<float>(height) / static_cast<float>(width),
+                             _projScaleFactor);
         }
+        projection.Translate(_translateX, _translateY);
 
         // 必要があればここで乗算
         if (_viewMatrix != NULL)
@@ -237,37 +159,15 @@ void LAppLive2DManager::OnUpdate() const
     }
 }
 
-void LAppLive2DManager::NextScene()
+void LAppLive2DManager::SetModel(std::string modelName, bool useOldParamId)
 {
-    csmInt32 no = (_sceneIndex + 1) % GetModelDirSize();
-    ChangeScene(no);
-}
-
-void LAppLive2DManager::ChangeScene(Csm::csmInt32 index)
-{
-    _sceneIndex = index;
-    if (DebugLogEnable)
-    {
-        LAppPal::PrintLogLn("[APP]model index: %d", _sceneIndex);
-    }
-
-    // ModelDir[]に保持したディレクトリ名から
-    // model3.jsonのパスを決定する.
-    // ディレクトリ名とmodel3.jsonの名前を一致させておくこと.
-    const csmString& model = _modelDir[index];
-    LAppPal::PrintLogLn("[APP]_modelDir: %s", model.GetRawString());
-
-    csmString modelPath(LAppDelegate::GetInstance()->GetExecuteAbsolutePath().c_str());
-    modelPath += ResourcesPath;
-    modelPath += model;
-    modelPath.Append(1, '/');
-
-    csmString modelJsonName(model);
+    std::string modelPath = LAppDelegate::GetInstance()->GetExecuteAbsolutePath() + ResourcesPath + modelName + "/";
+    std::string modelJsonName = modelName;
     modelJsonName += ".model3.json";
 
     ReleaseAllModel();
-    _models.PushBack(new LAppModel());
-    _models[0]->LoadAssets(modelPath.GetRawString(), modelJsonName.GetRawString());
+    _models.PushBack(new LAppModel(useOldParamId));
+    _models[0]->LoadAssets(modelPath.c_str(), modelJsonName.c_str());
 
     /*
      * モデル半透明表示を行うサンプルを提示する。
@@ -288,8 +188,8 @@ void LAppLive2DManager::ChangeScene(Csm:
 
 #if defined(USE_RENDER_TARGET) || defined(USE_MODEL_RENDER_TARGET)
         // モデル個別にαを付けるサンプルとして、もう1体モデルを作成し、少し位置をずらす
-        _models.PushBack(new LAppModel());
-        _models[1]->LoadAssets(modelPath.GetRawString(), modelJsonName.GetRawString());
+        _models.PushBack(new LAppModel(useOldParamId));
+        _models[1]->LoadAssets(modelPath.c_str(), modelJsonName.c_str());
         _models[1]->GetModelMatrix()->TranslateX(0.2f);
 #endif
 
@@ -317,3 +217,20 @@ void LAppLive2DManager::SetViewMatrix(Cu
         _viewMatrix->GetArray()[i] = m->GetArray()[i];
     }
 }
+
+void LAppLive2DManager::SetTracker(MouseCursorTracker *tracker)
+{
+    for (auto it = _models.Begin(); it != _models.End(); ++it)
+    {
+        (*it)->SetTracker(tracker);
+    }
+}
+
+void LAppLive2DManager::SetProjectionScaleTranslate(float scaleFactor,
+                                                    float translateX,
+                                                    float translateY)
+{
+    _projScaleFactor = scaleFactor;
+    _translateX = translateX;
+    _translateY = translateY;
+}
diff -pruN --exclude build ./demo_clean/src/LAppLive2DManager.hpp ./demo_dev/src/LAppLive2DManager.hpp
--- ./demo_clean/src/LAppLive2DManager.hpp	2025-05-30 00:59:58.256401196 +0100
+++ ./demo_dev/src/LAppLive2DManager.hpp	2025-05-30 01:12:54.056756547 +0100
@@ -6,12 +6,15 @@
  */
 #pragma once
 
+#include <string>
 #include <CubismFramework.hpp>
 #include <Math/CubismMatrix44.hpp>
 #include <Type/csmVector.hpp>
 
 class LAppModel;
 
+class MouseCursorTracker;
+
 /**
 * @brief サンプルアプリケーションにおいてCubismModelを管理するクラス<br>
 *         モデル生成と破棄、タップイベントの処理、モデル切り替えを行う。
@@ -36,24 +39,6 @@ public:
     static void ReleaseInstance();
 
     /**
-    * @brief   Resources フォルダにあるモデルフォルダ名をセットする
-    *
-    */
-    void SetUpModel();
-
-    /**
-    * @brief   Resources フォルダにあるモデルフォルダ名を取得する
-    *
-    */
-    Csm::csmVector<Csm::csmString> GetModelDir() const;
-
-    /**
-    * @brief   Resources フォルダにあるモデルフォルダのサイズを取得する
-    *
-    */
-    Csm::csmInt32 GetModelDirSize() const;
-
-    /**
     * @brief   現在のシーンで保持しているモデルを返す
     *
     * @param[in]   no  モデルリストのインデックス値
@@ -90,16 +75,14 @@ public:
     void OnUpdate() const;
 
     /**
-    * @brief   次のシーンに切り替える<br>
-    *           サンプルアプリケーションではモデルセットの切り替えを行う。
-    */
-    void NextScene();
-
-    /**
-    * @brief   シーンを切り替える<br>
-    *           サンプルアプリケーションではモデルセットの切り替えを行う。
-    */
-    void ChangeScene(Csm::csmInt32 index);
+     * @brief Set model data
+     *
+     * @param[in] modelName : Name of model, should be the same for both
+     *                        the directory and the model3.json file
+     * @param[in] useOldParamId : If true, translate new (Cubism 3+)
+     *                            parameter IDs to old (Cubism 2.1) ones
+     */
+    void SetModel(std::string modelName, bool useOldParamId);
 
     /**
      * @brief   モデル個数を得る
@@ -112,6 +95,24 @@ public:
      */
     void SetViewMatrix(Live2D::Cubism::Framework::CubismMatrix44* m);
 
+    /**
+     * @brief Set the pointer to the MouseCursorTracker instance
+     *
+     * @param[in] tracker : Pointer to MouseCursorTracker instance
+     */
+    void SetTracker(MouseCursorTracker *tracker);
+
+    /**
+     * @brief Set projection scale factor and translation parameters
+     *
+     * @param[in] scaleFactor : Scale factor applied in both X and Y directions
+     * @param[in] translateX : Translation in X direction
+     * @param[in] translateY : Translation in Y direction
+     */
+    void SetProjectionScaleTranslate(float scaleFactor,
+                                     float translateX,
+                                     float translateY);
+
 private:
     /**
     * @brief  コンストラクタ
@@ -125,7 +126,8 @@ private:
 
     Csm::CubismMatrix44* _viewMatrix; ///< モデル描画に用いるView行列
     Csm::csmVector<LAppModel*> _models; ///< モデルインスタンスのコンテナ
-    Csm::csmInt32 _sceneIndex; ///< 表示するシーンのインデックス値
 
-    Csm::csmVector<Csm::csmString> _modelDir; ///< モデルディレクトリ名のコンテナ
+    float _projScaleFactor;
+    float _translateX;
+    float _translateY;
 };
diff -pruN --exclude build ./demo_clean/src/LAppModel.cpp ./demo_dev/src/LAppModel.cpp
--- ./demo_clean/src/LAppModel.cpp	2025-05-30 00:59:58.256401196 +0100
+++ ./demo_dev/src/LAppModel.cpp	2025-05-30 01:17:12.780545257 +0100
@@ -21,26 +21,32 @@
 #include "LAppTextureManager.hpp"
 #include "LAppDelegate.hpp"
 
+#include "mouse_cursor_tracker.h"
+
+#include <iostream>
+
 using namespace Live2D::Cubism::Framework;
 using namespace Live2D::Cubism::Framework::DefaultParameterId;
 using namespace LAppDefine;
 
-LAppModel::LAppModel()
+LAppModel::LAppModel(bool useOldParamId)
     : LAppModel_Common()
     , _modelSetting(NULL)
     , _userTimeSeconds(0.0f)
+    , _tracker(nullptr)
+    , _useOldParamId(useOldParamId)
 {
     if (DebugLogEnable)
     {
         _debugMode = true;
     }
 
-    _idParamAngleX = CubismFramework::GetIdManager()->GetId(ParamAngleX);
-    _idParamAngleY = CubismFramework::GetIdManager()->GetId(ParamAngleY);
-    _idParamAngleZ = CubismFramework::GetIdManager()->GetId(ParamAngleZ);
-    _idParamBodyAngleX = CubismFramework::GetIdManager()->GetId(ParamBodyAngleX);
-    _idParamEyeBallX = CubismFramework::GetIdManager()->GetId(ParamEyeBallX);
-    _idParamEyeBallY = CubismFramework::GetIdManager()->GetId(ParamEyeBallY);
+    _idParamAngleX = CubismFramework::GetIdManager()->GetId(_(ParamAngleX));
+    _idParamAngleY = CubismFramework::GetIdManager()->GetId(_(ParamAngleY));
+    _idParamAngleZ = CubismFramework::GetIdManager()->GetId(_(ParamAngleZ));
+    _idParamBodyAngleX = CubismFramework::GetIdManager()->GetId(_(ParamBodyAngleX));
+    _idParamEyeBallX = CubismFramework::GetIdManager()->GetId(_(ParamEyeBallX));
+    _idParamEyeBallY = CubismFramework::GetIdManager()->GetId(_(ParamEyeBallY));
 }
 
 LAppModel::~LAppModel()
@@ -179,7 +185,7 @@ void LAppModel::SetupModel(ICubismModelS
         breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamAngleY, 0.0f, 8.0f, 3.5345f, 0.5f));
         breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamAngleZ, 0.0f, 10.0f, 5.5345f, 0.5f));
         breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamBodyAngleX, 0.0f, 4.0f, 15.5345f, 0.5f));
-        breathParameters.PushBack(CubismBreath::BreathParameterData(CubismFramework::GetIdManager()->GetId(ParamBreath), 0.5f, 0.5f, 3.2345f, 0.5f));
+        breathParameters.PushBack(CubismBreath::BreathParameterData(CubismFramework::GetIdManager()->GetId(_(ParamBreath)), 0.5f, 0.5f, 3.2345f, 0.5f));
 
         _breath->SetParameters(breathParameters);
     }
@@ -322,86 +328,117 @@ void LAppModel::Update()
     const csmFloat32 deltaTimeSeconds = LAppPal::GetDeltaTime();
     _userTimeSeconds += deltaTimeSeconds;
 
-    _dragManager->Update(deltaTimeSeconds);
-    _dragX = _dragManager->GetX();
-    _dragY = _dragManager->GetY();
-
-    // モーションによるパラメータ更新の有無
-    csmBool motionUpdated = false;
-
-    //-----------------------------------------------------------------
-    _model->LoadParameters(); // 前回セーブされた状態をロード
-    if (_motionManager->IsFinished())
-    {
-        // モーションの再生がない場合、待機モーションの中からランダムで再生する
-        StartRandomMotion(MotionGroupIdle, PriorityIdle);
-    }
-    else
+    if (_tracker)
     {
-        motionUpdated = _motionManager->UpdateMotion(_model, deltaTimeSeconds); // モーションを更新
-    }
-    _model->SaveParameters(); // 状態を保存
-    //-----------------------------------------------------------------
+        auto idMan = CubismFramework::GetIdManager();
+        auto params = _tracker->getParams();
 
-    // 不透明度
-    _opacity = _model->GetModelOpacity();
+        _model->LoadParameters(); // 前回セーブされた状態をロード
 
-    // まばたき
-    if (!motionUpdated)
-    {
-        if (_eyeBlink != NULL)
+        int paramsMotionPriority = static_cast<int>(params.motionPriority);
+
+        if (paramsMotionPriority != PriorityNone)
         {
-            // メインモーションの更新がないとき
-            _eyeBlink->UpdateParameters(_model, deltaTimeSeconds); // 目パチ
+            StartMotion(params.motionGroup.c_str(), params.motionNumber,
+                        paramsMotionPriority);
         }
-    }
+        else if (params.randomIdleMotion && _motionManager->IsFinished())
+        {
+            // モーションの再生がない場合、待機モーションの中からランダムで再生する
+            StartRandomMotion(MotionGroupIdle, PriorityIdle);
+        }
+        // FIXME pose does not return to normal after motion
+        // if we don't have randomIdleMotion set
+        else
+        {
+            _motionManager->UpdateMotion(_model, deltaTimeSeconds); // モーションを更新
+        }
+        _model->SaveParameters(); // 状態を保存
 
-    if (_expressionManager != NULL)
-    {
-        _expressionManager->UpdateMotion(_model, deltaTimeSeconds); // 表情でパラメータ更新（相対変化）
-    }
+        if (params.expression != "")
+        {
+            SetExpression(params.expression.c_str());
+        }
+        if (_expressionManager != NULL)
+        {
+            _expressionManager->UpdateMotion(_model, deltaTimeSeconds); // 表情でパラメータ更新（相対変化）
+        }
 
-    //ドラッグによる変化
-    //ドラッグによる顔の向きの調整
-    _model->AddParameterValue(_idParamAngleX, _dragX * 30); // -30から30の値を加える
-    _model->AddParameterValue(_idParamAngleY, _dragY * 30);
-    _model->AddParameterValue(_idParamAngleZ, _dragX * _dragY * -30);
+        bool autoBlink = params.autoBlink && _eyeBlink;
+        auto eyeLOpenIt = params.live2d.find("ParamEyeLOpen");
+        auto eyeROpenIt = params.live2d.find("ParamEyeROpen");
 
-    //ドラッグによる体の向きの調整
-    _model->AddParameterValue(_idParamBodyAngleX, _dragX * 10); // -10から10の値を加える
+        if (autoBlink)
+        {
+            // Handle blink first
+            _eyeBlink->UpdateParameters(_model, deltaTimeSeconds);
+        }
 
-    //ドラッグによる目の向きの調整
-    _model->AddParameterValue(_idParamEyeBallX, _dragX); // -1から1の値を加える
-    _model->AddParameterValue(_idParamEyeBallY, _dragY);
+        if (eyeLOpenIt != params.live2d.end())
+        {
+            // If value specified, override blinking
+            _model->SetParameterValue(idMan->GetId(_("ParamEyeLOpen")),
+                                      eyeLOpenIt->second);
+        }
+        else if (!autoBlink)
+        {
+            // If no value specified and no auto blink, set to 1
+            _model->SetParameterValue(idMan->GetId(_("ParamEyeLOpen")), 1);
 
-    // 呼吸など
-    if (_breath != NULL)
-    {
-        _breath->UpdateParameters(_model, deltaTimeSeconds);
-    }
+        }
 
-    // 物理演算の設定
-    if (_physics != NULL)
-    {
-        _physics->Evaluate(_model, deltaTimeSeconds);
-    }
+        if (eyeROpenIt != params.live2d.end())
+        {
+            _model->SetParameterValue(idMan->GetId(_("ParamEyeROpen")),
+                                      eyeROpenIt->second);
+        }
+        else if (!autoBlink)
+        {
+            _model->SetParameterValue(idMan->GetId(_("ParamEyeROpen")), 1);
+        }
 
-    // リップシンクの設定
-    if (_lipSync)
-    {
-        // リアルタイムでリップシンクを行う場合、システムから音量を取得して0〜1の範囲で値を入力します。
-        csmFloat32 value = 0.0f;
 
-        // 状態更新/RMS値取得
-        _wavFileHandler.Update(deltaTimeSeconds);
-        value = _wavFileHandler.GetRms();
+        if (params.useLipSync && _lipSync)
+        {
+            csmFloat32 value = params.lipSyncParam; // 0 to 1
 
-        for (csmUint32 i = 0; i < _lipSyncIds.GetSize(); ++i)
+            for (csmUint32 i = 0; i < _lipSyncIds.GetSize(); ++i)
+            {
+                _model->AddParameterValue(_lipSyncIds[i], value, 0.8f);
+            }
+        }
+        else
+        {
+            _model->SetParameterValue(idMan->GetId(_("ParamMouthOpenY")),
+                                      params.live2d["ParamMouthOpenY"]);
+        }
+
+        for (auto const &entry : params.live2d)
+        {
+            std::string key = entry.first;
+            double val = entry.second;
+
+            if (key != "ParamEyeLOpen" && key != "ParamEyeROpen" &&
+                key != "ParamMouthOpenY")
+            {
+                _model->SetParameterValue(idMan->GetId(_(key)), val);
+            }
+        }
+
+        if (params.autoBreath && _breath)
         {
-            _model->AddParameterValue(_lipSyncIds[i], value, 0.8f);
+            // Note: _model->LoadParameters and SaveParameters is needed
+            // before - see above.
+            _breath->UpdateParameters(_model, deltaTimeSeconds);
         }
     }
 
+    // 物理演算の設定
+    if (_physics != NULL)
+    {
+        _physics->Evaluate(_model, deltaTimeSeconds);
+    }
+
     // ポーズの設定
     if (_pose != NULL)
     {
@@ -464,7 +501,6 @@ CubismMotionQueueEntryHandle LAppModel::
     {
         csmString path = voice;
         path = _modelHomeDir + path;
-        _wavFileHandler.Start(path);
     }
 
     if (_debugMode)
@@ -616,3 +652,42 @@ Csm::Rendering::CubismOffscreenSurface_O
 {
     return _renderBuffer;
 }
+
+void LAppModel::SetTracker(MouseCursorTracker *tracker)
+{
+    _tracker = tracker;
+}
+
+Csm::ICubismModelSetting* LAppModel::GetModelSetting(void) const
+{
+    return _modelSetting;
+}
+
+Csm::csmString LAppModel::_(std::string s)
+{
+    std::string ans;
+    if (_useOldParamId)
+    {
+        if (s == "ParamTere")
+        {
+            ans = "PARAM_CHEEK";
+        }
+        else
+        {
+            for (size_t i = 0; i < s.size(); i++)
+            {
+                if (std::isupper(s[i]) && i != 0)
+                {
+                    ans += '_';
+                }
+                ans += std::toupper(s[i]);
+            }
+        }
+    }
+    else
+    {
+        ans = s;
+    }
+    return csmString(ans.c_str());
+}
+
diff -pruN --exclude build ./demo_clean/src/LAppModel.hpp ./demo_dev/src/LAppModel.hpp
--- ./demo_clean/src/LAppModel.hpp	2025-05-30 00:59:58.252401066 +0100
+++ ./demo_dev/src/LAppModel.hpp	2025-05-30 01:15:06.963872011 +0100
@@ -12,8 +12,8 @@
 #include <Type/csmRectF.hpp>
 #include <Rendering/OpenGL/CubismOffscreenSurface_OpenGLES2.hpp>
 
-#include "LAppWavFileHandler_Common.hpp"
 #include "LAppModel_Common.hpp"
+#include "mouse_cursor_tracker.h"
 
 /**
  * @brief ユーザーが実際に使用するモデルの実装クラス<br>
@@ -25,8 +25,11 @@ class LAppModel : public LAppModel_Commo
 public:
     /**
      * @brief コンストラクタ
+     *
+     * @param[in] useOldParamId : If true, translate new (Cubism 3+)
+     *                            parameter IDs to old (Cubism 2.1)  ones
      */
-    LAppModel();
+    LAppModel(bool useOldParamId);
 
     /**
      * @brief デストラクタ
@@ -116,6 +119,15 @@ public:
      */
     Csm::Rendering::CubismOffscreenSurface_OpenGLES2& GetRenderBuffer();
 
+    /**
+     * @brief Set the pointer to the MouseCursorTracker instance
+     *
+     * @param[in] tracker : Pointer to MouseCursorTracker instance
+     */
+    void SetTracker(MouseCursorTracker *tracker);
+
+    Csm::ICubismModelSetting* GetModelSetting(void) const;
+
 protected:
     /**
      *  @brief  モデルを描画する処理。モデルを描画する空間のView-Projection行列を渡す。
@@ -169,6 +181,17 @@ private:
     */
     void ReleaseExpressions();
 
+    /**
+     * @brief Translate new (Cubism 3+) parameter IDs to old (Cubism 2.1) ones
+     *
+     * @param[in] s : New parameter ID
+     *
+     * @return Old parameter ID
+     */
+    Csm::csmString _(std::string s);
+
+    bool _useOldParamId;
+
     Csm::ICubismModelSetting* _modelSetting; ///< モデルセッティング情報
     Csm::csmString _modelHomeDir; ///< モデルセッティングが置かれたディレクトリ
     Csm::csmFloat32 _userTimeSeconds; ///< デルタ時間の積算値[秒]
@@ -185,7 +208,7 @@ private:
     const Csm::CubismId* _idParamEyeBallX; ///< パラメータID: ParamEyeBallX
     const Csm::CubismId* _idParamEyeBallY; ///< パラメータID: ParamEyeBallXY
 
-    LAppWavFileHandler_Common _wavFileHandler; ///< wavファイルハンドラ
-
     Csm::Rendering::CubismOffscreenSurface_OpenGLES2 _renderBuffer;   ///< フレームバッファ以外の描画先
+
+    MouseCursorTracker *_tracker;
 };
diff -pruN --exclude build ./demo_clean/src/LAppTextureManager.cpp ./demo_dev/src/LAppTextureManager.cpp
--- ./demo_clean/src/LAppTextureManager.cpp	2025-05-30 00:59:58.252401066 +0100
+++ ./demo_dev/src/LAppTextureManager.cpp	2025-05-30 01:18:50.733874528 +0100
@@ -96,6 +96,46 @@ LAppTextureManager::TextureInfo* LAppTex
 
 }
 
+LAppTextureManager::TextureInfo* LAppTextureManager::CreateTextureFromColor(
+    uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha
+)
+{
+    int width = 8, height = 8;
+
+    uint8_t pixels[height][width][4];
+    for (std::size_t h = 0; h < height; h++)
+    {
+        for (std::size_t w = 0; w < width; w++)
+        {
+            pixels[h][w][0] = red;
+            pixels[h][w][1] = green;
+            pixels[h][w][2] = blue;
+            pixels[h][w][3] = alpha;
+        }
+    }
+
+    GLuint textureId;
+    glGenTextures(1, &textureId);
+    glBindTexture(GL_TEXTURE_2D, textureId);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+
+    glGenerateMipmap(GL_TEXTURE_2D);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glBindTexture(GL_TEXTURE_2D, 0);
+
+
+    LAppTextureManager::TextureInfo* textureInfo = new LAppTextureManager::TextureInfo();
+    textureInfo->fileName = "";
+    textureInfo->width = width;
+    textureInfo->height = height;
+    textureInfo->id = textureId;
+
+    _texturesInfo.PushBack(textureInfo);
+
+    return textureInfo;
+}
+
 void LAppTextureManager::ReleaseTextures()
 {
     for (Csm::csmUint32 i = 0; i < _texturesInfo.GetSize(); i++)
diff -pruN --exclude build ./demo_clean/src/LAppTextureManager.hpp ./demo_dev/src/LAppTextureManager.hpp
--- ./demo_clean/src/LAppTextureManager.hpp	2025-05-30 00:59:58.252401066 +0100
+++ ./demo_dev/src/LAppTextureManager.hpp	2025-05-30 01:19:14.566288429 +0100
@@ -41,6 +41,8 @@ public:
     */
     TextureInfo* CreateTextureFromPngFile(std::string fileName);
 
+    TextureInfo *CreateTextureFromColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255);
+
     /**
     * @brief 画像の解放
     *
diff -pruN --exclude build ./demo_clean/src/LAppView.cpp ./demo_dev/src/LAppView.cpp
--- ./demo_clean/src/LAppView.cpp	2025-05-30 00:59:58.252401066 +0100
+++ ./demo_dev/src/LAppView.cpp	2025-05-30 01:24:23.734104120 +0100
@@ -81,9 +81,6 @@ void LAppView::Initialize(int width, int
 void LAppView::Render()
 {
     _back->Render();
-    _gear->Render();
-    _power->Render();
-
 
     LAppLive2DManager* Live2DManager = LAppLive2DManager::GetInstance();
 
@@ -125,35 +122,17 @@ void LAppView::InitializeSprite()
     glfwGetWindowSize(LAppDelegate::GetInstance()->GetWindow(), &width, &height);
 
     LAppTextureManager* textureManager = LAppDelegate::GetInstance()->GetTextureManager();
-    const string resourcesPath = LAppDelegate::GetInstance()->GetExecuteAbsolutePath() + ResourcesPath;
 
-    string imageName = BackImageName;
-    LAppTextureManager::TextureInfo* backgroundTexture = textureManager->CreateTextureFromPngFile(resourcesPath + imageName);
+
+    LAppTextureManager::TextureInfo* backgroundTexture =
+        textureManager->CreateTextureFromColor(0, 255, 0);
 
     float x = width * 0.5f;
     float y = height * 0.5f;
-    float fWidth = static_cast<float>(backgroundTexture->width * 2.0f);
-    float fHeight = static_cast<float>(height) * 0.95f;
+    float fWidth = static_cast<float>(width);
+    float fHeight = static_cast<float>(height);
     _back = new LAppSprite(x, y, fWidth, fHeight, backgroundTexture->id, programId);
 
-    imageName = GearImageName;
-    LAppTextureManager::TextureInfo* gearTexture = textureManager->CreateTextureFromPngFile(resourcesPath + imageName);
-
-    x = static_cast<float>(width - gearTexture->width * 0.5f);
-    y = static_cast<float>(height - gearTexture->height * 0.5f);
-    fWidth = static_cast<float>(gearTexture->width);
-    fHeight = static_cast<float>(gearTexture->height);
-    _gear = new LAppSprite(x, y, fWidth, fHeight, gearTexture->id, programId);
-
-    imageName = PowerImageName;
-    LAppTextureManager::TextureInfo* powerTexture = textureManager->CreateTextureFromPngFile(resourcesPath + imageName);
-
-    x = static_cast<float>(width - powerTexture->width * 0.5f);
-    y = static_cast<float>(powerTexture->height * 0.5f);
-    fWidth = static_cast<float>(powerTexture->width);
-    fHeight = static_cast<float>(powerTexture->height);
-    _power = new LAppSprite(x, y, fWidth, fHeight, powerTexture->id, programId);
-
     // 画面全体を覆うサイズ
     x = width * 0.5f;
     y = height * 0.5f;
@@ -192,18 +171,6 @@ void LAppView::OnTouchesEnded(float px,
             LAppPal::PrintLogLn("[APP]touchesEnded x:%.2f y:%.2f", x, y);
         }
         live2DManager->OnTap(x, y);
-
-        // 歯車にタップしたか
-        if (_gear->IsHit(px, py))
-        {
-            live2DManager->NextScene();
-        }
-
-        // 電源ボタンにタップしたか
-        if (_power->IsHit(px, py))
-        {
-            LAppDelegate::GetInstance()->AppEnd();
-        }
     }
 }
 
@@ -329,32 +296,4 @@ void LAppView::ResizeSprite()
             _back->ResetRect(x, y, fWidth, fHeight);
         }
     }
-
-    if (_power)
-    {
-        GLuint id = _power->GetTextureId();
-        LAppTextureManager::TextureInfo* texInfo = textureManager->GetTextureInfoById(id);
-        if (texInfo)
-        {
-            x = static_cast<float>(width - texInfo->width * 0.5f);
-            y = static_cast<float>(texInfo->height * 0.5f);
-            fWidth = static_cast<float>(texInfo->width);
-            fHeight = static_cast<float>(texInfo->height);
-            _power->ResetRect(x, y, fWidth, fHeight);
-        }
-    }
-
-    if (_gear)
-    {
-        GLuint id = _gear->GetTextureId();
-        LAppTextureManager::TextureInfo* texInfo = textureManager->GetTextureInfoById(id);
-        if (texInfo)
-        {
-            x = static_cast<float>(width - texInfo->width * 0.5f);
-            y = static_cast<float>(height - texInfo->height * 0.5f);
-            fWidth = static_cast<float>(texInfo->width);
-            fHeight = static_cast<float>(texInfo->height);
-            _gear->ResetRect(x, y, fWidth, fHeight);
-        }
-    }
 }
diff -pruN --exclude build ./demo_clean/src/main.cpp ./demo_dev/src/main.cpp
--- ./demo_clean/src/main.cpp	2025-05-30 00:59:58.252401066 +0100
+++ ./demo_dev/src/main.cpp	2025-05-30 01:19:44.722858078 +0100
@@ -5,18 +5,186 @@
  * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
  */
 
+#include <thread>
+#include <stdexcept>
+#include <sstream>
+#include <vector>
+#include <utility>
+#include <string>
+
+#ifdef __cpp_lib_filesystem
+#include <filesystem>
+namespace fs = std::filesystem;
+#else
+#include <experimental/filesystem>
+namespace fs = std::experimental::filesystem;
+#endif
+
+#include "ICubismModelSetting.hpp"
 #include "LAppDelegate.hpp"
+#include "LAppLive2DManager.hpp"
+#include "LAppModel.hpp"
+#include "mouse_cursor_tracker.h"
+
+struct CmdArgs
+{
+    int windowWidth;
+    int windowHeight;
+    std::string windowTitle;
+    std::string rootDir;
+    float scaleFactor;
+    float translateX;
+    float translateY;
+    std::string modelName;
+    bool oldId; // If true, translate new (Cubism 3+) parameter IDs to old (Cubism 2.1) IDs
+    std::string cfgPath; // Path to config file for MouseCursorTracker
+};
+
+CmdArgs parseArgv(int argc, char *argv[])
+{
+    // I think the command-line args are simple enough to not justify using a library...
+    CmdArgs cmdArgs;
+    // Set default values
+    cmdArgs.windowWidth = 600;
+    cmdArgs.windowHeight = 600;
+    cmdArgs.windowTitle = "MouseTrackerForCubism example";
+    cmdArgs.rootDir = fs::current_path();
+    cmdArgs.scaleFactor = 4.5f;
+    cmdArgs.translateX = 0.0f;
+    cmdArgs.translateY = -3.1f;
+    cmdArgs.modelName = "Haru";
+    cmdArgs.oldId = false;
+    cmdArgs.cfgPath = "";
+
+    int i = 1;
+    while (i < argc)
+    {
+        std::string arg = argv[i];
+        std::stringstream ss;
+
+        if (arg == "--window-width" || arg == "-W") // capital W for consistency with height
+        {
+            ss << argv[i + 1];
+            if (!(ss >> cmdArgs.windowWidth))
+            {
+                throw std::runtime_error("Invalid argument for window width");
+            }
+        }
+        else if (arg == "--window-height" || arg == "-H") // avoiding "-h", typically for help
+        {
+            ss << argv[i + 1];
+            if (!(ss >> cmdArgs.windowHeight))
+            {
+                throw std::runtime_error("Invalid argument for window height");
+            }
+        }
+        else if (arg == "--window-title" || arg == "-t")
+        {
+            cmdArgs.windowTitle = argv[i + 1];
+        }
+        else if (arg == "--root-dir" || arg == "-d")
+        {
+            cmdArgs.rootDir = argv[i + 1];
+        }
+        else if (arg == "--scale-factor" || arg == "-f")
+        {
+            ss << argv[i + 1];
+            if (!(ss >> cmdArgs.scaleFactor))
+            {
+                throw std::runtime_error("Invalid argument for scale factor");
+            }
+        }
+        else if (arg == "--translate-x" || arg == "-x")
+        {
+            ss << argv[i + 1];
+            if (!(ss >> cmdArgs.translateX))
+            {
+                throw std::runtime_error("Invalid argument for translate X");
+            }
+        }
+        else if (arg == "--translate-y" || arg == "-y")
+        {
+            ss << argv[i + 1];
+            if (!(ss >> cmdArgs.translateY))
+            {
+                throw std::runtime_error("Invalid argument for translate Y");
+            }
+        }
+        else if (arg == "--model" || arg == "-m")
+        {
+            cmdArgs.modelName = argv[i + 1];
+        }
+        else if (arg == "--config" || arg == "-c")
+        {
+            cmdArgs.cfgPath = argv[i + 1];
+        }
+        else if (arg == "--old-param-id" || arg == "-o")
+        {
+            cmdArgs.oldId = (argv[i + 1][0] == '1');
+        }
+        else
+        {
+            throw std::runtime_error("Unrecognized argument: " + arg);
+        }
+
+        i += 2;
+    }
+
+    return cmdArgs;
+}
 
 int main(int argc, char* argv[])
 {
-    // create the application instance
-    if (LAppDelegate::GetInstance()->Initialize() == GL_FALSE)
+    auto cmdArgs = parseArgv(argc, argv);
+
+    LAppDelegate *delegate = LAppDelegate::GetInstance();
+
+    if (!delegate->Initialize(cmdArgs.windowWidth,
+                              cmdArgs.windowHeight,
+                              cmdArgs.windowTitle.c_str()))
     {
-        return 1;
+        throw std::runtime_error("Unable to initialize LAppDelegate");
     }
 
-    LAppDelegate::GetInstance()->Run();
+    LAppLive2DManager *manager = LAppLive2DManager::GetInstance();
+    manager->SetModel(cmdArgs.modelName, cmdArgs.oldId);
+
+    manager->SetProjectionScaleTranslate(cmdArgs.scaleFactor,
+                                         cmdArgs.translateX,
+                                         cmdArgs.translateY);
+
+    LAppModel *model = manager->GetModel(0);
+    if (!model) throw std::runtime_error("model is null");
+
+    Live2D::Cubism::Framework::ICubismModelSetting *modelSetting = model->GetModelSetting();
+    if (!modelSetting) throw std::runtime_error("modelSetting is null");
+
+    std::vector<std::pair<std::string, int> > motions;
+    int motionGroupCount = modelSetting->GetMotionGroupCount();
+    for (int i = 0; i < motionGroupCount; i++)
+    {
+        const char *motionGroup = modelSetting->GetMotionGroupName(i);
+        int motionCount = modelSetting->GetMotionCount(motionGroup);
+        motions.push_back(std::make_pair(std::string(motionGroup), motionCount));
+    }
+
+    std::vector<std::string> expressions;
+    int expCount = modelSetting->GetExpressionCount();
+    for (int i = 0; i < expCount; i++)
+    {
+        const char *expName = modelSetting->GetExpressionName(i);
+        expressions.push_back(std::string(expName));
+    }
+
+    MouseCursorTracker tracker(cmdArgs.cfgPath, motions, expressions);
+
+    std::thread trackerThread(&MouseCursorTracker::mainLoop, &tracker);
+    manager->SetTracker(&tracker);
+
+    delegate->Run();
+
+    tracker.stop();
+    trackerThread.join();
 
     return 0;
 }
-
