diff --git a/packages/backend/src/server/api/stream/channel.ts b/packages/backend/src/server/api/stream/channel.ts
index 93d50469022f5f44a80bfadd93252b9a58037fa5..047dedd5ce02b61200b30af08f63f5da1b337a51 100644
--- a/packages/backend/src/server/api/stream/channel.ts
+++ b/packages/backend/src/server/api/stream/channel.ts
@@ -103,11 +103,42 @@ export default abstract class Channel {
 
 	public onMessage?(type: string, body: JsonValue): void;
 
-	public async assignMyReaction(note: Packed<'Note'>, noteEntityService: NoteEntityService) {
-		if (this.user && Object.keys(note.reactions).length > 0) {
-			const myReaction = await noteEntityService.populateMyReaction(note, this.user.id);
-			note.myReaction = myReaction;
+	public async assignMyReaction(note: Packed<'Note'>, noteEntityService: NoteEntityService): Promise<Packed<'Note'>> {
+		let changed = false;
+		// StreamingApiServerService creates a single EventEmitter per server process,
+		// so a new note arriving from redis gets de-serialised once per server process,
+		// and then that single object is passed to all active channels on each connection.
+		// If we didn't clone the notes here, different connections would asynchronously write
+		// different values to the same object, resulting in a random value being sent to each frontend. -- Dakkar
+		const clonedNote = { ...note };
+		if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
+			if (note.renote && Object.keys(note.renote.reactions).length > 0) {
+				const myReaction = await noteEntityService.populateMyReaction(note.renote, this.user.id);
+				if (myReaction) {
+					changed = true;
+					clonedNote.renote = { ...note.renote };
+					clonedNote.renote.myReaction = myReaction;
+				}
+			}
+			if (note.renote?.reply && Object.keys(note.renote.reply.reactions).length > 0) {
+				const myReaction = await noteEntityService.populateMyReaction(note.renote.reply, this.user.id);
+				if (myReaction) {
+					changed = true;
+					clonedNote.renote = { ...note.renote };
+					clonedNote.renote.reply = { ...note.renote.reply };
+					clonedNote.renote.reply.myReaction = myReaction;
+				}
+			}
 		}
+		if (this.user && note.reply && Object.keys(note.reply.reactions).length > 0) {
+			const myReaction = await noteEntityService.populateMyReaction(note.reply, this.user.id);
+			if (myReaction) {
+				changed = true;
+				clonedNote.reply = { ...note.reply };
+				clonedNote.reply.myReaction = myReaction;
+			}
+		}
+		return changed ? clonedNote : note;
 	}
 }
 
diff --git a/packages/backend/src/server/api/stream/channels/bubble-timeline.ts b/packages/backend/src/server/api/stream/channels/bubble-timeline.ts
index b2745db92d277aa59506a6cffcd9a77032124a70..98ecf16a83de8a2f67b17ed882558748d48f5801 100644
--- a/packages/backend/src/server/api/stream/channels/bubble-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/bubble-timeline.ts
@@ -65,24 +65,11 @@ class BubbleTimelineChannel extends Channel {
 
 		if (this.isNoteMutedOrBlocked(note)) return;
 
-		const reactionsToFetch = [];
-		if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
-			if (note.renote) {
-				reactionsToFetch.push(this.assignMyReaction(note.renote, this.noteEntityService));
-				if (note.renote.reply) {
-					reactionsToFetch.push(this.assignMyReaction(note.renote.reply, this.noteEntityService));
-				}
-			}
-		}
-		if (this.user && note.reply) {
-			reactionsToFetch.push(this.assignMyReaction(note.reply, this.noteEntityService));
-		}
-
-		await Promise.all(reactionsToFetch);
-
-		this.connection.cacheNote(note);
-
-		this.send('note', note);
+		const clonedNote = await this.assignMyReaction(note, this.noteEntityService);
+
+		this.connection.cacheNote(clonedNote);
+
+		this.send('note', clonedNote);
 	}
 
 	@bindThis
diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts
index 8df59d906da7f4051887ba381403f4f3b7bef031..4443b20bed528fefca62e1c4ba1311eb3fb8a3ad 100644
--- a/packages/backend/src/server/api/stream/channels/global-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts
@@ -60,24 +60,11 @@ class GlobalTimelineChannel extends Channel {
 
 		if (this.isNoteMutedOrBlocked(note)) return;
 
-		const reactionsToFetch = [];
-		if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
-			if (note.renote) {
-				reactionsToFetch.push(this.assignMyReaction(note.renote, this.noteEntityService));
-				if (note.renote.reply) {
-					reactionsToFetch.push(this.assignMyReaction(note.renote.reply, this.noteEntityService));
-				}
-			}
-		}
-		if (this.user && note.reply) {
-			reactionsToFetch.push(this.assignMyReaction(note.reply, this.noteEntityService));
-		}
-
-		await Promise.all(reactionsToFetch);
-
-		this.connection.cacheNote(note);
-
-		this.send('note', note);
+		const clonedNote = await this.assignMyReaction(note, this.noteEntityService);
+
+		this.connection.cacheNote(clonedNote);
+
+		this.send('note', clonedNote);
 	}
 
 	@bindThis
diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts
index f48eff85c95de8e492939c28f11a87267e25552e..af1b17b533823f1347b479d385fdb573c724a892 100644
--- a/packages/backend/src/server/api/stream/channels/home-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts
@@ -81,24 +81,11 @@ class HomeTimelineChannel extends Channel {
 
 		if (this.isNoteMutedOrBlocked(note)) return;
 
-		const reactionsToFetch = [];
-		if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
-			if (note.renote) {
-				reactionsToFetch.push(this.assignMyReaction(note.renote, this.noteEntityService));
-				if (note.renote.reply) {
-					reactionsToFetch.push(this.assignMyReaction(note.renote.reply, this.noteEntityService));
-				}
-			}
-		}
-		if (this.user && note.reply) {
-			reactionsToFetch.push(this.assignMyReaction(note.reply, this.noteEntityService));
-		}
-
-		await Promise.all(reactionsToFetch);
+		const clonedNote = await this.assignMyReaction(note, this.noteEntityService);
 
-		this.connection.cacheNote(note);
+		this.connection.cacheNote(clonedNote);
 
-		this.send('note', note);
+		this.send('note', clonedNote);
 	}
 
 	@bindThis
diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
index 8c58b2518eb8023e408b5182a4ee48f57a0f0eed..7c604c0b58516ef6243a67bcf7fb4e10005ad10e 100644
--- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
@@ -98,24 +98,11 @@ class HybridTimelineChannel extends Channel {
 			}
 		}
 
-		const reactionsToFetch = [];
-		if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
-			if (note.renote) {
-				reactionsToFetch.push(this.assignMyReaction(note.renote, this.noteEntityService));
-				if (note.renote.reply) {
-					reactionsToFetch.push(this.assignMyReaction(note.renote.reply, this.noteEntityService));
-				}
-			}
-		}
-		if (this.user && note.reply) {
-			reactionsToFetch.push(this.assignMyReaction(note.reply, this.noteEntityService));
-		}
-
-		await Promise.all(reactionsToFetch);
+		const clonedNote = await this.assignMyReaction(note, this.noteEntityService);
 
-		this.connection.cacheNote(note);
+		this.connection.cacheNote(clonedNote);
 
-		this.send('note', note);
+		this.send('note', clonedNote);
 	}
 
 	@bindThis
diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts
index cb832bd3c294292a72963450360ac51d9097f375..2d48b6ecfb0eac2bbdaceff725ce6baa83590703 100644
--- a/packages/backend/src/server/api/stream/channels/local-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts
@@ -70,24 +70,11 @@ class LocalTimelineChannel extends Channel {
 
 		if (this.isNoteMutedOrBlocked(note)) return;
 
-		const reactionsToFetch = [];
-		if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
-			if (note.renote) {
-				reactionsToFetch.push(this.assignMyReaction(note.renote, this.noteEntityService));
-				if (note.renote.reply) {
-					reactionsToFetch.push(this.assignMyReaction(note.renote.reply, this.noteEntityService));
-				}
-			}
-		}
-		if (this.user && note.reply) {
-			reactionsToFetch.push(this.assignMyReaction(note.reply, this.noteEntityService));
-		}
-
-		await Promise.all(reactionsToFetch);
+		const clonedNote = await this.assignMyReaction(note, this.noteEntityService);
 
-		this.connection.cacheNote(note);
+		this.connection.cacheNote(clonedNote);
 
-		this.send('note', note);
+		this.send('note', clonedNote);
 	}
 
 	@bindThis