diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6e4bf822..91c37aa2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Import/export a muted users
 - Proper handling of deletes when using websocket streaming
 - Added optimistic chat message sending, so you can start writing next message before the previous one has been sent
+- Added a small red badge to the favicon when there's unread notifications
 
 ### Fixed
 - Fixed clicking NSFW hider through status popover
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
index 3cbbf020..b472fcf6 100644
--- a/src/boot/after_store.js
+++ b/src/boot/after_store.js
@@ -7,6 +7,7 @@ import { getOrCreateApp, getClientToken } from '../services/new_api/oauth.js'
 import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
 import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
 import { applyTheme } from '../services/style_setter/style_setter.js'
+import FaviconService from '../services/favicon_service/favicon_service.js'
 
 let staticInitialResults = null
 
@@ -326,6 +327,8 @@ const afterStoreSetup = async ({ store, i18n }) => {
   const width = windowWidth()
   store.dispatch('setMobileLayout', width <= 800)
 
+  FaviconService.initFaviconService()
+
   const overrides = window.___pleromafe_dev_overrides || {}
   const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin
   store.dispatch('setInstanceOption', { name: 'server', value: server })
diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js
index 4b479e13..49258563 100644
--- a/src/components/notifications/notifications.js
+++ b/src/components/notifications/notifications.js
@@ -6,6 +6,7 @@ import {
   filteredNotificationsFromStore,
   unseenNotificationsFromStore
 } from '../../services/notification_utils/notification_utils.js'
+import FaviconService from '../../services/favicon_service/favicon_service.js'
 import { library } from '@fortawesome/fontawesome-svg-core'
 import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
 
@@ -75,8 +76,10 @@ const Notifications = {
   watch: {
     unseenCountTitle (count) {
       if (count > 0) {
+        FaviconService.drawFaviconBadge()
         this.$store.dispatch('setPageTitle', `(${count})`)
       } else {
+        FaviconService.clearFaviconBadge()
         this.$store.dispatch('setPageTitle', '')
       }
     }
diff --git a/src/services/favicon_service/favicon_service.js b/src/services/favicon_service/favicon_service.js
new file mode 100644
index 00000000..d1ddee41
--- /dev/null
+++ b/src/services/favicon_service/favicon_service.js
@@ -0,0 +1,61 @@
+import { find } from 'lodash'
+
+const createFaviconService = () => {
+  let favimg, favcanvas, favcontext, favicon
+  const faviconWidth = 128
+  const faviconHeight = 128
+  const badgeRadius = 32
+
+  const initFaviconService = () => {
+    const nodes = document.getElementsByTagName('link')
+    favicon = find(nodes, node => node.rel === 'icon')
+    if (favicon) {
+      favcanvas = document.createElement('canvas')
+      favcanvas.width = faviconWidth
+      favcanvas.height = faviconHeight
+      favimg = new Image()
+      favimg.src = favicon.href
+      favcontext = favcanvas.getContext('2d')
+    }
+  }
+
+  const isImageLoaded = (img) => img.complete && img.naturalHeight !== 0
+
+  const clearFaviconBadge = () => {
+    if (!favimg || !favcontext || !favicon) return
+
+    favcontext.clearRect(0, 0, faviconWidth, faviconHeight)
+    if (isImageLoaded(favimg)) {
+      favcontext.drawImage(favimg, 0, 0, favimg.width, favimg.height, 0, 0, faviconWidth, faviconHeight)
+    }
+    favicon.href = favcanvas.toDataURL('image/png')
+  }
+
+  const drawFaviconBadge = () => {
+    if (!favimg || !favcontext || !favcontext) return
+
+    clearFaviconBadge()
+
+    const style = getComputedStyle(document.body)
+    const badgeColor = `${style.getPropertyValue('--badgeNotification') || 'rgb(240, 100, 100)'}`
+
+    if (isImageLoaded(favimg)) {
+      favcontext.drawImage(favimg, 0, 0, favimg.width, favimg.height, 0, 0, faviconWidth, faviconHeight)
+    }
+    favcontext.fillStyle = badgeColor
+    favcontext.beginPath()
+    favcontext.arc(faviconWidth - badgeRadius, badgeRadius, badgeRadius, 0, 2 * Math.PI, false)
+    favcontext.fill()
+    favicon.href = favcanvas.toDataURL('image/png')
+  }
+
+  return {
+    initFaviconService,
+    clearFaviconBadge,
+    drawFaviconBadge
+  }
+}
+
+const FaviconService = createFaviconService()
+
+export default FaviconService