diff --git a/packages/client/package.json b/packages/client/package.json
index 9f86a471b0..2c9b3cbb21 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -31,6 +31,7 @@
 		"eventemitter3": "5.0.0",
 		"idb-keyval": "6.2.0",
 		"insert-text-at-cursor": "0.3.0",
+		"is-file-animated": "1.0.1",
 		"json5": "2.2.1",
 		"katex": "0.15.6",
 		"matter-js": "0.18.0",
diff --git a/packages/client/src/scripts/upload.ts b/packages/client/src/scripts/upload.ts
index 51f1c1b86f..9a39652ef5 100644
--- a/packages/client/src/scripts/upload.ts
+++ b/packages/client/src/scripts/upload.ts
@@ -1,6 +1,7 @@
 import { reactive, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import { readAndCompressImage } from 'browser-image-resizer';
+import { getCompressionConfig } from './upload/compress-config';
 import { defaultStore } from '@/store';
 import { apiUrl } from '@/config';
 import { $i } from '@/account';
@@ -16,12 +17,6 @@ type Uploading = {
 };
 export const uploads = ref<Uploading[]>([]);
 
-const compressTypeMap = {
-	'image/jpeg': { quality: 0.85, mimeType: 'image/jpeg' },
-	'image/webp': { quality: 0.85, mimeType: 'image/jpeg' },
-	'image/svg+xml': { quality: 1, mimeType: 'image/png' },
-} as const;
-
 const mimeTypeMap = {
 	'image/webp': 'webp',
 	'image/jpeg': 'jpg',
@@ -34,16 +29,18 @@ export function uploadFile(
 	name?: string,
 	keepOriginal: boolean = defaultStore.state.keepOriginalUploading,
 ): Promise<Misskey.entities.DriveFile> {
+	if ($i == null) throw new Error('Not logged in');
+
 	if (folder && typeof folder === 'object') folder = folder.id;
 
 	return new Promise((resolve, reject) => {
 		const id = Math.random().toString();
 
 		const reader = new FileReader();
-		reader.onload = async (ev) => {
+		reader.onload = async (): Promise<void> => {
 			const ctx = reactive<Uploading>({
 				id: id,
-				name: name || file.name || 'untitled',
+				name: name ?? file.name ?? 'untitled',
 				progressMax: undefined,
 				progressValue: undefined,
 				img: window.URL.createObjectURL(file),
@@ -51,20 +48,22 @@ export function uploadFile(
 
 			uploads.value.push(ctx);
 
-			let resizedImage: any;
-			if (!keepOriginal && file.type in compressTypeMap) {
-				const imgConfig = compressTypeMap[file.type];
-
-				const config = {
-					maxWidth: 2048,
-					maxHeight: 2048,
-					debug: true,
-					...imgConfig,
-				};
-
+			const config = !keepOriginal ? await getCompressionConfig(file) : undefined;
+			let resizedImage: Blob | undefined;
+			if (config) {
 				try {
-					resizedImage = await readAndCompressImage(file, config);
-					ctx.name = file.type !== imgConfig.mimeType ? `${ctx.name}.${mimeTypeMap[compressTypeMap[file.type].mimeType]}` : ctx.name;
+					const resized = await readAndCompressImage(file, config);
+					if (resized.size < file.size || file.type === 'image/webp') {
+						// The compression may not always reduce the file size
+						// (and WebP is not browser safe yet)
+						resizedImage = resized;
+					}
+					if (_DEV_) {
+						const saved = ((1 - resized.size / file.size) * 100).toFixed(2);
+						console.log(`Image compression: before ${file.size} bytes, after ${resized.size} bytes, saved ${saved}%`);
+					}
+
+					ctx.name = file.type !== config.mimeType ? `${ctx.name}.${mimeTypeMap[config.mimeType]}` : ctx.name;
 				} catch (err) {
 					console.error('Failed to resize image', err);
 				}
@@ -73,13 +72,13 @@ export function uploadFile(
 			const formData = new FormData();
 			formData.append('i', $i.token);
 			formData.append('force', 'true');
-			formData.append('file', resizedImage || file);
+			formData.append('file', resizedImage ?? file);
 			formData.append('name', ctx.name);
 			if (folder) formData.append('folderId', folder);
 
 			const xhr = new XMLHttpRequest();
 			xhr.open('POST', apiUrl + '/drive/files/create', true);
-			xhr.onload = (ev) => {
+			xhr.onload = ((ev: ProgressEvent<XMLHttpRequest>) => {
 				if (xhr.status !== 200 || ev.target == null || ev.target.response == null) {
 					// TODO: 消すのではなくて(ネットワーク的なエラーなら)再送できるようにしたい
 					uploads.value = uploads.value.filter(x => x.id !== id);
@@ -122,7 +121,7 @@ export function uploadFile(
 				resolve(driveFile);
 
 				uploads.value = uploads.value.filter(x => x.id !== id);
-			};
+			}) as (ev: ProgressEvent<EventTarget>) => any;
 
 			xhr.upload.onprogress = ev => {
 				if (ev.lengthComputable) {
diff --git a/packages/client/src/scripts/upload/compress-config.ts b/packages/client/src/scripts/upload/compress-config.ts
new file mode 100644
index 0000000000..793c78ad20
--- /dev/null
+++ b/packages/client/src/scripts/upload/compress-config.ts
@@ -0,0 +1,23 @@
+import isAnimated from 'is-file-animated';
+import type { BrowserImageResizerConfig } from 'browser-image-resizer';
+
+const compressTypeMap = {
+	'image/jpeg': { quality: 0.85, mimeType: 'image/jpeg' },
+	'image/png': { quality: 1, mimeType: 'image/png' },
+	'image/webp': { quality: 0.85, mimeType: 'image/jpeg' },
+	'image/svg+xml': { quality: 1, mimeType: 'image/png' },
+} as const;
+
+export async function getCompressionConfig(file: File): Promise<BrowserImageResizerConfig | undefined> {
+	const imgConfig = compressTypeMap[file.type];
+	if (!imgConfig || await isAnimated(file)) {
+		return;
+	}
+
+	return {
+		maxWidth: 2048,
+		maxHeight: 2048,
+		debug: true,
+		...imgConfig,
+	};
+}
diff --git a/yarn.lock b/yarn.lock
index dc9ac7ccd8..96db703b28 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4975,6 +4975,7 @@ __metadata:
     eventemitter3: 5.0.0
     idb-keyval: 6.2.0
     insert-text-at-cursor: 0.3.0
+    is-file-animated: 1.0.1
     json5: 2.2.1
     katex: 0.15.6
     matter-js: 0.18.0
@@ -9574,6 +9575,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"is-file-animated@npm:1.0.1":
+  version: 1.0.1
+  resolution: "is-file-animated@npm:1.0.1"
+  checksum: bcc281e0694e1ba74adfdef75f83f1637ab6470eceecef867d21b4a98e112c32188514b3172348dd137b82cbe8771b6d683de1439d8e1e86011fed77da896c4e
+  languageName: node
+  linkType: hard
+
 "is-fullwidth-code-point@npm:^1.0.0":
   version: 1.0.0
   resolution: "is-fullwidth-code-point@npm:1.0.0"