diff --git a/CMakeLists.txt b/CMakeLists.txt
index 62420ab..e029074 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,4 +18,5 @@ install(FILES config/kwinrulesrc DESTINATION /etc/xdg)
 install(DIRECTORY scripts/cutefishlauncher DESTINATION /usr/share/kwin/scripts)
 install(DIRECTORY scripts/cutefish_squash DESTINATION /usr/share/kwin/effects)
 install(DIRECTORY scripts/cutefish_scale DESTINATION /usr/share/kwin/effects)
+install(DIRECTORY scripts/cutefish_popups DESTINATION /usr/share/kwin/effects)
 install(DIRECTORY tabbox/cutefish_thumbnail DESTINATION /usr/share/kwin/tabbox)
diff --git a/config/kwinrc b/config/kwinrc
index 956dd7f..d4bc97f 100644
--- a/config/kwinrc
+++ b/config/kwinrc
@@ -1,6 +1,9 @@
+[Compositing]
+OpenGLIsUnsafe=false
+
 [Plugins]
 blurEnabled=true
-slidingpopupsEnabled=true
+kwin4_effect_fadingpopupsEnabled=false
 kwin4_effect_dialogparentEnabled=true
 
 kwin4_effect_fadeEnabled=false
@@ -12,8 +15,8 @@ kwin4_effect_translucencyEnabled=false
 magiclampEnabled=false
 
 [Effect-Blur]
-BlurStrength=5
-NoiseStrength=0
+BlurStrength=7
+NoiseStrength=1
 
 [Windows]
 FocusStealingPreventionLevel=0
@@ -30,8 +33,8 @@ TabBox=false
 TabBoxAlternative=false
 
 [Effect-SlidingPopups]
-SlideInTime=250
-SlideOutTime=250
+SlideInTime=300
+SlideOutTime=300
 
 [TabBox]
 ActivitiesMode=1
@@ -50,6 +53,12 @@ SwitchingMode=0
 [ElectricBorders]
 TopLeft=None
 
+[TouchEdges]
+Bottom=None
+Left=None
+Right=None
+Top=None
+
 [org.kde.kdecoration2]
 BorderSize=Normal
 ButtonsOnLeft=
diff --git a/scripts/cutefish_popups/contents/code/main.js b/scripts/cutefish_popups/contents/code/main.js
new file mode 100644
index 0000000..ca6d6ee
--- /dev/null
+++ b/scripts/cutefish_popups/contents/code/main.js
@@ -0,0 +1,146 @@
+/*
+    KWin - the KDE window manager
+    This file is part of the KDE project.
+
+    SPDX-FileCopyrightText: 2018 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
+
+    SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+"use strict";
+
+var blocklist = [
+    // The logout screen has to be animated only by the logout effect.
+    "ksmserver ksmserver",
+    "ksmserver-logout-greeter ksmserver-logout-greeter",
+
+    // KDE Plasma splash screen has to be animated only by the login effect.
+    "ksplashqml ksplashqml"
+];
+
+var allowlist = [
+    "cutefish-launcher cutefish-launcher"
+];
+
+function isPopupWindow(window) {
+    // If the window is blocklisted, don't animate it.
+    if (blocklist.indexOf(window.windowClass) != -1) {
+        return false;
+    }
+
+    if (allowlist.indexOf(window.windowClass) != -1) {
+        return true;
+    }
+
+    // Animate combo box popups, tooltips, popup menus, etc.
+    if (window.popupWindow) {
+        return true;
+    }
+
+    // Maybe the outline deserves its own effect.
+    if (window.outline) {
+        return true;
+    }
+
+    // Override-redirect windows are usually used for user interface
+    // concepts that are expected to be animated by this effect, e.g.
+    // popups that contain window thumbnails on X11, etc.
+    if (!window.managed) {
+        // Some utility windows can look like popup windows (e.g. the
+        // address bar dropdown in Firefox), but we don't want to fade
+        // them because the fade effect didn't do that.
+        if (window.utility) {
+            return false;
+        }
+
+        return true;
+    }
+
+    // Previously, there was a "monolithic" fade effect, which tried to
+    // animate almost every window that was shown or hidden. Then it was
+    // split into two effects: one that animates toplevel windows and
+    // this one. In addition to popups, this effect also animates some
+    // special windows(e.g. notifications) because the monolithic version
+    // was doing that.
+    if (window.dock || window.splash || window.toolbar
+            || window.notification || window.onScreenDisplay
+            || window.criticalNotification) {
+        return true;
+    }
+
+    return false;
+}
+
+var cutefishPopupsEffect = {
+    loadConfig: function () {
+        cutefishPopupsEffect.fadeInDuration = animationTime(150);
+        cutefishPopupsEffect.fadeOutDuration = animationTime(150) * 4;
+    },
+    slotWindowAdded: function (window) {
+        if (effects.hasActiveFullScreenEffect) {
+            return;
+        }
+        if (!isPopupWindow(window)) {
+            return;
+        }
+        if (!window.visible) {
+            return;
+        }
+        if (!effect.grab(window, Effect.WindowAddedGrabRole)) {
+            return;
+        }
+        window.fadeInAnimation = animate({
+            window: window,
+            curve: QEasingCurve.Linear,
+            duration: cutefishPopupsEffect.fadeInDuration,
+            type: Effect.Opacity,
+            from: 0.0,
+            to: 1.0
+        });
+    },
+    slotWindowClosed: function (window) {
+        if (effects.hasActiveFullScreenEffect) {
+            return;
+        }
+        if (!isPopupWindow(window)) {
+            return;
+        }
+        if (!window.visible) {
+            return;
+        }
+        if (!effect.grab(window, Effect.WindowClosedGrabRole)) {
+            return;
+        }
+        window.fadeOutAnimation = animate({
+            window: window,
+            curve: QEasingCurve.OutQuart,
+            duration: cutefishPopupsEffect.fadeOutDuration,
+            type: Effect.Opacity,
+            from: 1.0,
+            to: 0.0
+        });
+    },
+    slotWindowDataChanged: function (window, role) {
+        if (role == Effect.WindowAddedGrabRole) {
+            if (window.fadeInAnimation && effect.isGrabbed(window, role)) {
+                cancel(window.fadeInAnimation);
+                delete window.fadeInAnimation;
+            }
+        } else if (role == Effect.WindowClosedGrabRole) {
+            if (window.fadeOutAnimation && effect.isGrabbed(window, role)) {
+                cancel(window.fadeOutAnimation);
+                delete window.fadeOutAnimation;
+            }
+        }
+    },
+    init: function () {
+        cutefishPopupsEffect.loadConfig();
+
+        effect.configChanged.connect(cutefishPopupsEffect.loadConfig);
+        effects.windowAdded.connect(cutefishPopupsEffect.slotWindowAdded);
+        effects.windowClosed.connect(cutefishPopupsEffect.slotWindowClosed);
+        effects.windowDataChanged.connect(cutefishPopupsEffect.slotWindowDataChanged);
+    }
+};
+
+cutefishPopupsEffect.init();
diff --git a/scripts/cutefish_popups/metadata.desktop b/scripts/cutefish_popups/metadata.desktop
new file mode 100644
index 0000000..234b0c1
--- /dev/null
+++ b/scripts/cutefish_popups/metadata.desktop
@@ -0,0 +1,14 @@
+[Desktop Entry]
+Name=CutefishOS Fading Popups
+Type=Service
+X-KDE-ServiceTypes=KWin/Effect
+X-KDE-PluginInfo-Author=Vlad Zahorodnii
+X-KDE-PluginInfo-Email=vlad.zahorodnii@kde.org
+X-KDE-PluginInfo-Name=cutefish_popups
+X-KDE-PluginInfo-Version=1.0
+X-KDE-PluginInfo-Category=Appearance
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=true
+X-KDE-Ordering=60
+X-Plasma-API=javascript
+X-Plasma-MainScript=code/main.js
\ No newline at end of file
diff --git a/scripts/cutefish_popups/metadata.json b/scripts/cutefish_popups/metadata.json
new file mode 100644
index 0000000..0fb5037
--- /dev/null
+++ b/scripts/cutefish_popups/metadata.json
@@ -0,0 +1,20 @@
+{
+    "KPlugin": {
+        "Authors": [
+            {
+                "Email": "vlad.zahorodnii@kde.org",
+                "Name": "Vlad Zahorodnii"
+            }
+        ],
+        "Category": "Appearance",
+        "Description": "Make popups smoothly fade in and out when they are shown or hidden",
+        "ServiceTypes": [
+            "KWin/Effect"
+        ],
+        "EnabledByDefault": true,
+        "Version": "1.0"
+    },
+    "X-KDE-Ordering": "60",
+    "X-Plasma-API": "javascript",
+    "X-Plasma-MainScript": "code/main.js"
+}
\ No newline at end of file
diff --git a/scripts/cutefish_scale/contents/code/main.js b/scripts/cutefish_scale/contents/code/main.js
index c360772..6ba1c0d 100644
--- a/scripts/cutefish_scale/contents/code/main.js
+++ b/scripts/cutefish_scale/contents/code/main.js
@@ -24,7 +24,7 @@ var blocklist = [
 
 var scaleEffect = {
     loadConfig: function (window) {
-        var defaultDuration = 200;
+        var defaultDuration = 250;
         var duration = effect.readConfig("Duration", defaultDuration) || defaultDuration;
         scaleEffect.duration = animationTime(duration);
         scaleEffect.inScale = effect.readConfig("InScale", 0.96);