diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index c94da97747..fe098c9de6 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -12,7 +12,7 @@
 					tabindex="0"
 					@click="chosen(emoji, $event)"
 				>
-					<MkEmoji class="emoji" :emoji="`:${emoji.name}:`" />
+					<MkEmoji class="emoji" :emoji="`:${emoji.name}:`"/>
 				</button>
 			</div>
 			<div v-if="searchResultUnicode.length > 0" class="body">
@@ -81,7 +81,7 @@ import { ref, computed, watch, onMounted } from 'vue';
 import * as Misskey from 'misskey-js';
 import XSection from '@/components/MkEmojiPicker.section.vue';
 import { emojilist, UnicodeEmojiDef, unicodeEmojiCategories as categories } from '@/scripts/emojilist';
-import Ripple from '@/components/MkRipple.vue';
+import MkRippleEffect from '@/components/MkRippleEffect.vue';
 import * as os from '@/os';
 import { isTouchUsing } from '@/scripts/touch';
 import { deviceKind } from '@/scripts/device-kind';
@@ -288,7 +288,7 @@ function chosen(emoji: any, ev?: MouseEvent) {
 		const rect = el.getBoundingClientRect();
 		const x = rect.left + (el.offsetWidth / 2);
 		const y = rect.top + (el.offsetHeight / 2);
-		os.popup(Ripple, { x, y }, {}, 'end');
+		os.popup(MkRippleEffect, { x, y }, {}, 'end');
 	}
 
 	const key = getKey(emoji);
diff --git a/packages/frontend/src/components/MkPlusOneEffect.vue b/packages/frontend/src/components/MkPlusOneEffect.vue
new file mode 100644
index 0000000000..a0bb22db92
--- /dev/null
+++ b/packages/frontend/src/components/MkPlusOneEffect.vue
@@ -0,0 +1,66 @@
+<template>
+<div :class="$style.root" :style="{ zIndex, top: `${y - 64}px`, left: `${x - 64}px` }">
+	<span class="text" :class="{ up }">+1</span>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { onMounted } from 'vue';
+import * as os from '@/os';
+
+const props = withDefaults(defineProps<{
+	x: number;
+	y: number;
+}>(), {
+});
+
+const emit = defineEmits<{
+	(ev: 'end'): void;
+}>();
+
+let up = $ref(false);
+const zIndex = os.claimZIndex('high');
+
+onMounted(() => {
+	window.setTimeout(() => {
+		up = true;
+	}, 10);
+
+	window.setTimeout(() => {
+		emit('end');
+	}, 1100);
+});
+</script>
+
+<style lang="scss" module>
+.root {
+	pointer-events: none;
+	position: fixed;
+	width: 128px;
+	height: 128px;
+
+	&:global {
+		> .text {
+			display: block;
+			height: 1em;
+			text-align: center;
+			position: absolute;
+			top: 0;
+			left: 0;
+			right: 0;
+			bottom: 0;
+			margin: auto;
+			color: var(--accent);
+			font-size: 18px;
+			font-weight: bold;
+			transform: translateY(-30px);
+			transition: transform 1s cubic-bezier(0,.5,0,1), opacity 1s cubic-bezier(.5,0,1,.5);
+
+			&.up {
+				opacity: 0;
+				transform: translateY(-50px);
+			}
+		}
+	}
+}
+</style>
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index b8784620c0..5088de7353 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -97,7 +97,7 @@ import { instance } from '@/instance';
 import { $i, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account';
 import { uploadFile } from '@/scripts/upload';
 import { deepClone } from '@/scripts/clone';
-import Ripple from '@/components/MkRipple.vue';
+import MkRippleEffect from '@/components/MkRippleEffect.vue';
 
 const modal = inject('modal');
 
@@ -575,7 +575,7 @@ async function post(ev?: MouseEvent) {
 		const rect = el.getBoundingClientRect();
 		const x = rect.left + (el.offsetWidth / 2);
 		const y = rect.top + (el.offsetHeight / 2);
-		os.popup(Ripple, { x, y }, {}, 'end');
+		os.popup(MkRippleEffect, { x, y }, {}, 'end');
 	}
 
 	let postData = {
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index f3c77231db..b10c7009f5 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -19,6 +19,7 @@ import XReactionIcon from '@/components/MkReactionIcon.vue';
 import * as os from '@/os';
 import { useTooltip } from '@/scripts/use-tooltip';
 import { $i } from '@/account';
+import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue';
 
 const props = defineProps<{
 	reaction: string;
@@ -57,7 +58,10 @@ const toggleReaction = () => {
 const anime = () => {
 	if (document.hidden) return;
 
-	// TODO: 新しくリアクションが付いたことが視覚的に分かりやすいアニメーション
+	const rect = buttonRef.value.getBoundingClientRect();
+	const x = rect.left + (buttonRef.value.offsetWidth / 2);
+	const y = rect.top + (buttonRef.value.offsetHeight / 2);
+	os.popup(MkPlusOneEffect, { x, y }, {}, 'end');
 };
 
 watch(() => props.count, (newCount, oldCount) => {
diff --git a/packages/frontend/src/components/MkRipple.vue b/packages/frontend/src/components/MkRippleEffect.vue
similarity index 100%
rename from packages/frontend/src/components/MkRipple.vue
rename to packages/frontend/src/components/MkRippleEffect.vue
diff --git a/packages/frontend/src/components/form/checkbox.vue b/packages/frontend/src/components/form/checkbox.vue
index ba3b2dc146..d869b600c9 100644
--- a/packages/frontend/src/components/form/checkbox.vue
+++ b/packages/frontend/src/components/form/checkbox.vue
@@ -23,7 +23,7 @@
 <script lang="ts" setup>
 import { toRefs, Ref } from 'vue';
 import * as os from '@/os';
-import Ripple from '@/components/MkRipple.vue';
+import MkRippleEffect from '@/components/MkRippleEffect.vue';
 import { i18n } from '@/i18n';
 
 const props = defineProps<{
@@ -45,7 +45,7 @@ const toggle = () => {
 		const rect = button.getBoundingClientRect();
 		const x = rect.left + (button.offsetWidth / 2);
 		const y = rect.top + (button.offsetHeight / 2);
-		os.popup(Ripple, { x, y, particle: false }, {}, 'end');
+		os.popup(MkRippleEffect, { x, y, particle: false }, {}, 'end');
 	}
 };
 </script>
diff --git a/packages/frontend/src/directives/ripple.ts b/packages/frontend/src/directives/ripple.ts
index d32f7ab441..5611777347 100644
--- a/packages/frontend/src/directives/ripple.ts
+++ b/packages/frontend/src/directives/ripple.ts
@@ -1,4 +1,4 @@
-import Ripple from '@/components/MkRipple.vue';
+import MkRippleEffect from '@/components/MkRippleEffect.vue';
 import { popup } from '@/os';
 
 export default {
@@ -12,7 +12,7 @@ export default {
 			const x = rect.left + (el.offsetWidth / 2);
 			const y = rect.top + (el.offsetHeight / 2);
 
-			popup(Ripple, { x, y }, {}, 'end');
+			popup(MkRippleEffect, { x, y }, {}, 'end');
 		});
 	},
 };