diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml
index c6e5329c265c644eeeea5f3570f3f9f899c7b6dc..9a32dac94e0b57cc1c4dc675cb5ddf04952373fe 100644
--- a/.github/workflows/nodejs.yml
+++ b/.github/workflows/nodejs.yml
@@ -29,6 +29,8 @@ jobs:
 
     steps:
     - uses: actions/checkout@v2
+      with:
+        submodules: true
     - name: Use Node.js ${{ matrix.node-version }}
       uses: actions/setup-node@v1
       with:
@@ -48,6 +50,8 @@ jobs:
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v2
+      with:
+        submodules: true
     - uses: actions/setup-node@v1
       with:
         node-version: 12.x
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..9246e09b8bd4d018397d2e3628aa92a25f9c4f44
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "misskey-assets"]
+	path = misskey-assets
+	url = https://github.com/misskey-dev/assets.git
diff --git a/Dockerfile b/Dockerfile
index 1e8584551fd72c700e51740e61804c28d46d90fd..8c655c4c4fb6824257ed790b4446c68b2af2f9fa 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -20,6 +20,7 @@ RUN apk add --no-cache \
     python3 \
     zlib-dev
 
+RUN git submodule update --init
 COPY package.json yarn.lock .yarnrc ./
 RUN yarn install
 COPY . ./
diff --git a/docs/docker.en.md b/docs/docker.en.md
index 93eee8e4b4abd3a154585c5bd375b593c98faf16..adeafe3d31bca365744c3485bf9796fa384b8e7d 100644
--- a/docs/docker.en.md
+++ b/docs/docker.en.md
@@ -83,10 +83,11 @@ Just `docker-compose up -d`. GLHF!
 1. `git stash`
 2. `git checkout master`
 3. `git pull`
-4. `git stash pop`
-5. `docker-compose build`
-6. Check [ChangeLog](../CHANGELOG.md) for migration information
-7. `docker-compose stop && docker-compose up -d`
+4. `git submodule update --init`
+5. `git stash pop`
+6. `docker-compose build`
+7. Check [ChangeLog](../CHANGELOG.md) for migration information
+8. `docker-compose stop && docker-compose up -d`
 
 ### How to execute [cli commands](manage.en.md):
 `docker-compose run --rm web node built/tools/mark-admin @example`
diff --git a/docs/docker.fr.md b/docs/docker.fr.md
index 7abd46335f94b4202a08d8ffe7dfe12d6649c37b..840e5b5a285110251225ec8ab2bd491d14578c30 100644
--- a/docs/docker.fr.md
+++ b/docs/docker.fr.md
@@ -50,10 +50,11 @@ Utilisez la commande `docker-compose up -d`. GLHF!
 1. `git stash`
 2. `git checkout master`
 3. `git pull`
-4. `git stash pop`
-5. `docker-compose build`
-6. Consultez le [ChangeLog](../CHANGELOG.md) pour avoir les éventuelles informations de migration
-7. `docker-compose stop && docker-compose up -d`
+4. `git submodule update --init`
+5. `git stash pop`
+6. `docker-compose build`
+7. Consultez le [ChangeLog](../CHANGELOG.md) pour avoir les éventuelles informations de migration
+8. `docker-compose stop && docker-compose up -d`
 
 ### Comment exécuter des [commandes](manage.fr.md)
 `docker-compose run --rm web node built/tools/mark-admin @example`
diff --git a/docs/docker.ja.md b/docs/docker.ja.md
index 15b1a944acc7fb2ea1fe17214845b6e4bbaab5f2..c660a9041bce99d819f72eaa3fa0433e713daf50 100644
--- a/docs/docker.ja.md
+++ b/docs/docker.ja.md
@@ -83,10 +83,11 @@ docker-compose run --rm web yarn run init
 1. `git stash`
 2. `git checkout master`
 3. `git pull`
-4. `git stash pop`
-5. `docker-compose build`
-6. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する
-7. `docker-compose stop && docker-compose up -d`
+4. `git submodule update --init`
+5. `git stash pop`
+6. `docker-compose build`
+7. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する
+8. `docker-compose stop && docker-compose up -d`
 
 ### cliコマンドを実行する方法:
 
diff --git a/docs/docker.zh.md b/docs/docker.zh.md
index a842fcb0cc46e44fd79f384d84f17e628c6fd631..5a494ea11e02b9c52a2f0588a3f94c01bc4f7b68 100644
--- a/docs/docker.zh.md
+++ b/docs/docker.zh.md
@@ -83,10 +83,11 @@ docker-compose run --rm web yarn run init
 1. `git stash`
 2. `git checkout master`
 3. `git pull`
-4. `git stash pop`
-5. `docker-compose build`
-6. 检查 [更新日志](../CHANGELOG.md) 以获取升级迁移信息。
-7. `docker-compose stop && docker-compose up -d`
+4. `git submodule update --init`
+5. `git stash pop`
+6. `docker-compose build`
+7. 检查 [更新日志](../CHANGELOG.md) 以获取升级迁移信息。
+8. `docker-compose stop && docker-compose up -d`
 
 ### 如何执行 [控制台指令](manage.zh.md):
 `docker-compose run --rm web node built/tools/mark-admin @example`
diff --git a/docs/setup.en.md b/docs/setup.en.md
index bb72fcda1e6250886f0bf168e63dc4762347d6ff..dfe50a6600a574562d4f80808f53a700ed2b2cd2 100644
--- a/docs/setup.en.md
+++ b/docs/setup.en.md
@@ -131,11 +131,12 @@ You can check if the service is running with `systemctl status misskey`.
 ### How to update your Misskey server to the latest version
 1. `git checkout master`
 2. `git pull`
-3. `yarn install`
-4. `NODE_ENV=production yarn build`
-5. `yarn migrate`
-6. Restart your Misskey process to apply changes
-7. Enjoy
+3. `git submodule update --init`
+4. `yarn install`
+5. `NODE_ENV=production yarn build`
+6. `yarn migrate`
+7. Restart your Misskey process to apply changes
+8. Enjoy
 
 If you encounter any problems with updating, please try the following:
 1. `yarn clean` or `yarn cleanall`
diff --git a/docs/setup.fr.md b/docs/setup.fr.md
index 7f4795eec56b9cf6420b0c432a32603f632b845e..f38c7a8eab90e8429446eb4b6e5e8208f7b495a2 100644
--- a/docs/setup.fr.md
+++ b/docs/setup.fr.md
@@ -126,9 +126,10 @@ Vous pouvez vérifier si le service a démarré en utilisant la commande `system
 ### Méthode de mise à jour vers la plus récente version de Misskey
 1. `git checkout master`
 2. `git pull`
-3. `yarn install`
-4. `NODE_ENV=production yarn build`
-5. `yarn migrate`
+3. `git submodule update --init`
+4. `yarn install`
+5. `NODE_ENV=production yarn build`
+6. `yarn migrate`
 
 ----------------------------------------------------------------
 
diff --git a/docs/setup.ja.md b/docs/setup.ja.md
index e8bae56f43b433a8b9c484b260cf30ec62ef5645..5681ee8c511eeac151aa598ed8685dbfe5e8c32a 100644
--- a/docs/setup.ja.md
+++ b/docs/setup.ja.md
@@ -133,9 +133,10 @@ yarn run init
 ### Misskeyを最新バージョンにアップデートする方法:
 1. `git checkout master`
 2. `git pull`
-3. `yarn install`
-4. `NODE_ENV=production yarn build`
-5. `yarn migrate`
+3. `git submodule update --init`
+4. `yarn install`
+5. `NODE_ENV=production yarn build`
+6. `yarn migrate`
 
 なにか問題が発生した場合は、`yarn clean`または`yarn cleanall`すると直る場合があります。
 
diff --git a/docs/setup.zh.md b/docs/setup.zh.md
index 50664706b757826a5b9b002b96ac0b06a0ac5e31..26a72f0d05a1ba81783698cf0c35b55be78374c1 100644
--- a/docs/setup.zh.md
+++ b/docs/setup.zh.md
@@ -131,11 +131,12 @@ yarn run init
 ### 如何将您的 Misskey 服务器升级至最新版本
 1. `git checkout master`
 2. `git pull`
-3. `yarn install`
-4. `NODE_ENV=production yarn build`
-5. `yarn migrate`
-6. 重启您的 Misskey 进程来应用改变。
-7. 尽情享受吧!
+3. `git submodule update --init`
+4. `yarn install`
+5. `NODE_ENV=production yarn build`
+6. `yarn migrate`
+7. 重启您的 Misskey 进程来应用改变。
+8. 尽情享受吧!
 
 如果您在更新时遇到任何问题,请尝试以下操作:
 1. `yarn clean` 或是 `yarn cleanall`
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index b456f7b1d42e645a6bc32a4ccd213637206b74ee..428cfc1fd699ee2fdd74d9db59a2060ce5b86cb3 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -779,6 +779,7 @@ translate: "翻訳"
 translatedFrom: "{x}から翻訳"
 accountDeletionInProgress: "アカウントの削除が進行中です"
 usernameInfo: "サーバー上であなたのアカウントを一意に識別するための名前。アルファベット(a~z, A~Z)、数字(0~9)、およびアンダーバー(_)が使用できます。ユーザー名は後から変更することは出来ません。"
+aiChanMode: "藍モード"
 keepCw: "CWを維持する"
 
 _accountDelete:
@@ -1189,6 +1190,7 @@ _widgets:
   jobQueue: "ジョブキュー"
   serverMetric: "サーバーメトリクス"
   aiscript: "AiScriptコンソール"
+  aichan: "藍"
 
 _cw:
   hide: "隠す"
diff --git a/misskey-assets b/misskey-assets
new file mode 160000
index 0000000000000000000000000000000000000000..0179793ec891856d6f37a3be16ba4c22f67a81b5
--- /dev/null
+++ b/misskey-assets
@@ -0,0 +1 @@
+Subproject commit 0179793ec891856d6f37a3be16ba4c22f67a81b5
diff --git a/src/client/pages/settings/general.vue b/src/client/pages/settings/general.vue
index cfa8107d28d321eda162b0d32b7a8677dc8947bb..f8e8e6b24b86deda6649c889ad911fd6c6180dd0 100644
--- a/src/client/pages/settings/general.vue
+++ b/src/client/pages/settings/general.vue
@@ -45,6 +45,10 @@
 		</FormSwitch>
 	</FormGroup>
 
+	<FormGroup>
+		<FormSwitch v-model:value="aiChanMode">{{ $ts.aiChanMode }}</FormSwitch>
+	</FormGroup>
+
 	<FormRadios v-model="fontSize">
 		<template #desc>{{ $ts.fontSize }}</template>
 		<option value="small"><span style="font-size: 14px;">Aa</span></option>
@@ -149,6 +153,7 @@ export default defineComponent({
 		enableInfiniteScroll: defaultStore.makeGetterSetter('enableInfiniteScroll'),
 		useReactionPickerForContextMenu: defaultStore.makeGetterSetter('useReactionPickerForContextMenu'),
 		squareAvatars: defaultStore.makeGetterSetter('squareAvatars'),
+		aiChanMode: defaultStore.makeGetterSetter('aiChanMode'),
 	},
 
 	watch: {
@@ -184,6 +189,10 @@ export default defineComponent({
 			this.reloadAsk();
 		},
 
+		aiChanMode() {
+			this.reloadAsk();
+		},
+
 		showGapBetweenNotesInTimeline() {
 			this.reloadAsk();
 		},
diff --git a/src/client/store.ts b/src/client/store.ts
index 4c4a7d93e997d452dba6b8dacb9a816905f212d9..e4b762873db414aba70f5aab8654995497987ac1 100644
--- a/src/client/store.ts
+++ b/src/client/store.ts
@@ -210,6 +210,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: ''
 	},
+	aiChanMode: {
+		where: 'device',
+		default: false
+	},
 }));
 
 // TODO: 他のタブと永続化されたstateを同期
diff --git a/src/client/tsconfig.json b/src/client/tsconfig.json
index ac7ef7ddfcafd68d1952aedf3377a7a429b7b416..7a26047ddf823238bb93dd58c7b845986a2da2d9 100644
--- a/src/client/tsconfig.json
+++ b/src/client/tsconfig.json
@@ -21,7 +21,8 @@
 		"baseUrl": ".",
 		"paths": {
 			"@/*": ["../*"],
-			"@client/*": ["./*"]
+			"@client/*": ["./*"],
+			"@lib/*": ["../../lib/*"],
 		},
 		"typeRoots": [
 			"node_modules/@types",
diff --git a/src/client/ui/default.vue b/src/client/ui/default.vue
index 23b63114cc77031c4897e2f2d03b649f8c9686f9..eef693faef7b49342880ce6cb7e65ef5c3da3832 100644
--- a/src/client/ui/default.vue
+++ b/src/client/ui/default.vue
@@ -54,12 +54,14 @@
 		<XWidgets v-if="widgetsShowing" class="tray"/>
 	</transition>
 
+	<iframe v-if="$store.state.aiChanMode" class="ivnzpscs" ref="live2d" src="https://misskey-dev.github.io/mascot-web/?scale=2&y=1.4"></iframe>
+
 	<XCommon/>
 </div>
 </template>
 
 <script lang="ts">
-import { defineComponent, defineAsyncComponent } from 'vue';
+import { defineComponent, defineAsyncComponent, markRaw } from 'vue';
 import { instanceName } from '@client/config';
 import { StickySidebar } from '@client/scripts/sticky-sidebar';
 import XSidebar from './default.sidebar.vue';
@@ -131,6 +133,19 @@ export default defineComponent({
 			this.isMobile = (window.innerWidth <= MOBILE_THRESHOLD);
 			this.isDesktop = (window.innerWidth >= DESKTOP_THRESHOLD);
 		}, { passive: true });
+
+		if (this.$store.state.aiChanMode) {
+			const iframeRect = this.$refs.live2d.getBoundingClientRect();
+			window.addEventListener('mousemove', ev => {
+				this.$refs.live2d.contentWindow.postMessage({
+					type: 'moveCursor',
+					body: {
+						x: ev.clientX - iframeRect.left,
+						y: ev.clientY - iframeRect.top,
+					}
+				}, '*');
+			}, { passive: true });
+		}
 	},
 
 	methods: {
@@ -201,6 +216,10 @@ export default defineComponent({
 				}
 			}], e);
 		},
+
+		onAiClick(ev) {
+			//if (this.live2d) this.live2d.click(ev);
+		}
 	}
 });
 </script>
@@ -458,5 +477,15 @@ export default defineComponent({
 		overflow: auto;
 		background: var(--bg);
 	}
+
+	> .ivnzpscs {
+		position: fixed;
+		bottom: 0;
+		right: 0;
+		width: 300px;
+		height: 600px;
+		border: none;
+		pointer-events: none;
+	}
 }
 </style>
diff --git a/src/client/widgets/aichan.vue b/src/client/widgets/aichan.vue
new file mode 100644
index 0000000000000000000000000000000000000000..06c49090a15231adf6301ff2e8b82ac31641ef0b
--- /dev/null
+++ b/src/client/widgets/aichan.vue
@@ -0,0 +1,59 @@
+<template>
+<MkContainer :naked="props.transparent" :show-header="false">
+	<iframe class="dedjhjmo" ref="live2d" @click="touched" src="https://misskey-dev.github.io/mascot-web/?scale=1.5&y=1.1&eyeY=100"></iframe>
+</MkContainer>
+</template>
+
+<script lang="ts">
+import { defineComponent, markRaw } from 'vue';
+import define from './define';
+import MkContainer from '@client/components/ui/container.vue';
+import * as os from '@client/os';
+
+const widget = define({
+	name: 'ai',
+	props: () => ({
+		transparent: {
+			type: 'boolean',
+			default: false,
+		},
+	})
+});
+
+export default defineComponent({
+	extends: widget,
+	components: {
+		MkContainer,
+	},
+	data() {
+		return {
+		};
+	},
+	mounted() {
+		window.addEventListener('mousemove', ev => {
+			const iframeRect = this.$refs.live2d.getBoundingClientRect();
+			this.$refs.live2d.contentWindow.postMessage({
+				type: 'moveCursor',
+				body: {
+					x: ev.clientX - iframeRect.left,
+					y: ev.clientY - iframeRect.top,
+				}
+			}, '*');
+		}, { passive: true });
+	},
+	methods: {
+		touched() {
+			//if (this.live2d) this.live2d.changeExpression('gurugurume');
+		}
+	}
+});
+</script>
+
+<style lang="scss" scoped>
+.dedjhjmo {
+	width: 100%;
+	height: 350px;
+	border: none;
+	pointer-events: none;
+}
+</style>
diff --git a/src/client/widgets/index.ts b/src/client/widgets/index.ts
index 38cb85494a725cd02e00e5f409303951ccd5717c..51a82af080ad08efdbac7dd9ce9a56419ab1b4a3 100644
--- a/src/client/widgets/index.ts
+++ b/src/client/widgets/index.ts
@@ -19,6 +19,7 @@ export default function(app: App) {
 	app.component('MkwJobQueue', defineAsyncComponent(() => import('./job-queue.vue')));
 	app.component('MkwButton', defineAsyncComponent(() => import('./button.vue')));
 	app.component('MkwAiscript', defineAsyncComponent(() => import('./aiscript.vue')));
+	app.component('MkwAichan', defineAsyncComponent(() => import('./aichan.vue')));
 }
 
 export const widgets = [
@@ -40,4 +41,5 @@ export const widgets = [
 	'jobQueue',
 	'button',
 	'aiscript',
+	'aichan',
 ];
diff --git a/webpack.config.ts b/webpack.config.ts
index 296813caa0131ba5e238d9bfd9002a6e105c8aa1..e9f3aa6e4788ea3cf3fd683207f708ddc9fbedba 100644
--- a/webpack.config.ts
+++ b/webpack.config.ts
@@ -164,6 +164,7 @@ module.exports = {
 		],
 		alias: {
 			'@client': __dirname + '/src/client',
+			'@lib': __dirname + '/lib',
 			'@': __dirname + '/src',
 			'const.styl': __dirname + '/src/client/const.styl'
 		}