diff --git a/content/blog/2024-10-01-holo.md b/content/blog/2024-10-01-holo.md
index 3c2adef..638575f 100644
--- a/content/blog/2024-10-01-holo.md
+++ b/content/blog/2024-10-01-holo.md
@@ -13,3 +13,211 @@ title = "3Dホログラムを作った"
ここまではっきり映るとは思ってなくて驚いた。余ってるスマホを何に使おうと思ってたので、こういうのを表示させておくといいかも。ただし、表記は反転させないといけないみたい。
+
+## react + tsx + three-vrm
+
+一応、最小限のコードを載せておきます。反転は対応しています。
+
+```json:package.json
+{
+ "name": "holoai",
+ "version": "0.1.0",
+ "private": true,
+ "dependencies": {
+ "@pixiv/three-vrm": "^3.1.1",
+ "@pixiv/three-vrm-animation": "^3.1.1",
+ "@react-three/drei": "^9.114.0",
+ "@react-three/fiber": "^8.17.9",
+ "@react-three/postprocessing": "^2.16.3",
+ "@testing-library/jest-dom": "^5.17.0",
+ "@testing-library/react": "^13.4.0",
+ "@testing-library/user-event": "^13.5.0",
+ "@types/jest": "^27.5.2",
+ "@types/node": "^16.18.112",
+ "@types/react": "^18.3.10",
+ "@types/react-dom": "^18.3.0",
+ "@types/three": "^0.167.2",
+ "axios": "^1.7.7",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-scripts": "5.0.1",
+ "three": "^0.167.1",
+ "three-stdlib": "^2.30.5",
+ "typescript": "^4.9.5",
+ "web-vitals": "^2.1.4"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test",
+ "eject": "react-scripts eject"
+ },
+ "eslintConfig": {
+ "extends": [
+ "react-app",
+ "react-app/jest"
+ ]
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ }
+}
+```
+
+```css:src/index.css
+.time {
+ transform:scale(-1,1);
+ position: absolute;
+ top: 10px;
+ padding: 10px;
+ z-index: 100;
+ color: #e9ff00;
+ font-size: 30px;
+ text-align: center;
+}
+```
+
+```tsx:src/pages/time.tsx
+import React, { useState, useEffect } from 'react';
+
+//function reverseString(str: string): string {
+// return str.split('').reverse().join('');
+//}
+
+const ScreenTimeCanvas: React.FC = () => {
+ const [currentDateTime, setCurrentDateTime] = useState('');
+ //const [reversedDateTime, setReversedDateTime] = useState('');
+
+ useEffect(() => {
+ const updateDateTime = () => {
+ const now = new Date();
+ const formatted = now.toLocaleString("ja-JP", {
+ year: 'numeric',
+ month: '2-digit',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit'
+ });
+ setCurrentDateTime(formatted);
+ //setReversedDateTime(reverseString(formatted));
+ };
+ updateDateTime();
+ const timer = setInterval(updateDateTime, 1000);
+ return () => clearInterval(timer);
+ }, []);
+
+ return (
+
+ );
+};
+
+export default ScreenTimeCanvas;
+```
+
+```tsx:src/pages/vrm.tsx
+import * as THREE from 'three'
+import React, { useState, useEffect, useRef } from 'react';
+import { OrbitControls } from '@react-three/drei'
+import { useFrame, Canvas } from '@react-three/fiber';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
+import { VRM, VRMUtils, VRMLoaderPlugin } from '@pixiv/three-vrm';
+import { VRMAnimationLoaderPlugin, VRMAnimation, createVRMAnimationClip } from "@pixiv/three-vrm-animation";
+
+interface ModelProps {
+ url: string
+ url_anim: string
+ scale: [number, number, number]
+ position: [number, number, number]
+ rotation: [number, number, number]
+}
+
+const VRMModel: React.FC = ({ url, url_anim, position, rotation, scale }) => {
+
+ const [vrm, setVrm] = useState(null);
+ const mixerRef = useRef(null);
+
+ useEffect(() => {
+ const loader = new GLTFLoader();
+ loader.register((parser) => new VRMLoaderPlugin(parser));
+ loader.register((parser) => new VRMAnimationLoaderPlugin(parser));
+ loader.load(url, (gltf) => {
+ const vrmModel = gltf.userData.vrm as VRM;
+ VRMUtils.removeUnnecessaryJoints(vrmModel.scene);
+ setVrm(vrmModel);
+ const mixer = new THREE.AnimationMixer(vrmModel.scene);
+ mixerRef.current = mixer;
+ loader.load(url_anim, (animGltf) => {
+ const vrmAnimations = animGltf.userData.vrmAnimations as VRMAnimation[];
+ if (vrmAnimations && vrmAnimations.length > 0) {
+ const clip = createVRMAnimationClip(vrmAnimations[0], vrmModel);
+ mixer.clipAction(clip).play();
+ }
+ });
+ });
+ }, [url, url_anim]);
+
+ useFrame((state, delta) => {
+ if (mixerRef.current) mixerRef.current.update(delta);
+ if (vrm) vrm.update(delta);
+ });
+
+ return vrm ? : null;
+};
+
+export const VRMModelCanvas = () => {
+ return (
+
+
+
+ )
+}
+export default VRMModelCanvas;
+```
+
+```tsx:src/App.tsx
+import React from 'react'
+import VRMModelCanvas from './pages/vrm'
+import ScreenTimeCanvas from './pages/time'
+
+const App = () => {
+ return (
+ <>
+
+
+ >
+ )
+}
+
+export default App;
+```