diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp
index d1ae6df94a..91e3842fa9 100644
--- a/apps/launcher/advancedpage.cpp
+++ b/apps/launcher/advancedpage.cpp
@@ -203,6 +203,7 @@ bool Launcher::AdvancedPage::loadSettings()
             showOwnedComboBox->setCurrentIndex(showOwnedIndex);
         loadSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
         loadSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
+        scalingSpinBox->setValue(mEngineSettings.getFloat("scaling factor", "GUI"));
     }
 
     // Bug fixes
@@ -360,6 +361,9 @@ void Launcher::AdvancedPage::saveSettings()
             mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex);
         saveSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
         saveSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
+        float uiScalingFactor = scalingSpinBox->value();
+        if (uiScalingFactor != mEngineSettings.getFloat("scaling factor", "GUI"))
+            mEngineSettings.setFloat("scaling factor", "GUI", uiScalingFactor);
     }
 
     // Bug fixes
diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp
index 9bbea9c519..31d188879f 100644
--- a/apps/openmw/mwbase/windowmanager.hpp
+++ b/apps/openmw/mwbase/windowmanager.hpp
@@ -171,6 +171,8 @@ namespace MWBase
             virtual void setDragDrop(bool dragDrop) = 0;
             virtual bool getWorldMouseOver() = 0;
 
+            virtual float getScalingFactor() = 0;
+
             virtual bool toggleFogOfWar() = 0;
 
             virtual bool toggleFullHelp() = 0;
diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp
index 6152efaa98..402f7656cb 100644
--- a/apps/openmw/mwgui/inventorywindow.cpp
+++ b/apps/openmw/mwgui/inventorywindow.cpp
@@ -67,13 +67,8 @@ namespace MWGui
         , mLastYSize(0)
         , mPreview(new MWRender::InventoryPreview(parent, resourceSystem, MWMechanics::getPlayer()))
         , mTrading(false)
-        , mScaleFactor(1.0f)
         , mUpdateTimer(0.f)
     {
-        float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
-        if (uiScale > 0.f)
-            mScaleFactor = uiScale;
-
         mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture()));
         mPreview->rebuild();
 
@@ -469,10 +464,11 @@ namespace MWGui
         MyGUI::IntSize size = mAvatarImage->getSize();
         int width = std::min(mPreview->getTextureWidth(), size.width);
         int height = std::min(mPreview->getTextureHeight(), size.height);
-        mPreview->setViewport(int(width*mScaleFactor), int(height*mScaleFactor));
+        float scalingFactor = MWBase::Environment::get().getWindowManager()->getScalingFactor();
+        mPreview->setViewport(int(width*scalingFactor), int(height*scalingFactor));
 
         mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f,
-                                                                     width*mScaleFactor/float(mPreview->getTextureWidth()), height*mScaleFactor/float(mPreview->getTextureHeight())));
+                                                                     width*scalingFactor/float(mPreview->getTextureWidth()), height*scalingFactor/float(mPreview->getTextureHeight())));
     }
 
     void InventoryWindow::onNameFilterChanged(MyGUI::EditBox* _sender)
@@ -637,8 +633,9 @@ namespace MWGui
         y = (mAvatarImage->getHeight()-1) - y;
 
         // Scale coordinates
-        x = int(x*mScaleFactor);
-        y = int(y*mScaleFactor);
+        float scalingFactor = MWBase::Environment::get().getWindowManager()->getScalingFactor();
+        x = static_cast<int>(x*scalingFactor);
+        y = static_cast<int>(y*scalingFactor);
 
         int slot = mPreview->getSlotSelected (x, y);
 
diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp
index dc3ee9e0cb..214245767b 100644
--- a/apps/openmw/mwgui/inventorywindow.hpp
+++ b/apps/openmw/mwgui/inventorywindow.hpp
@@ -104,7 +104,6 @@ namespace MWGui
             std::unique_ptr<MWRender::InventoryPreview> mPreview;
 
             bool mTrading;
-            float mScaleFactor;
             float mUpdateTimer;
 
             void toggleMaximized();
diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp
index 54c09e00f1..2f9d357bde 100644
--- a/apps/openmw/mwgui/windowmanagerimp.cpp
+++ b/apps/openmw/mwgui/windowmanagerimp.cpp
@@ -1,5 +1,6 @@
 #include "windowmanagerimp.hpp"
 
+#include <algorithm>
 #include <cassert>
 #include <chrono>
 #include <thread>
@@ -187,8 +188,8 @@ namespace MWGui
       , mVersionDescription(versionDescription)
       , mWindowVisible(true)
     {
-        float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
-        mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), uiScale);
+        mScalingFactor = std::clamp(Settings::Manager::getFloat("scaling factor", "GUI"), 0.5f, 8.f);
+        mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), mScalingFactor);
         mGuiPlatform->initialise(resourcePath, (boost::filesystem::path(logpath) / "MyGUI.log").generic_string());
 
         mGui = new MyGUI::Gui;
@@ -199,7 +200,7 @@ namespace MWGui
         MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag);
 
         // Load fonts
-        mFontLoader.reset(new Gui::FontLoader(encoding, resourceSystem->getVFS(), userDataPath));
+        mFontLoader.reset(new Gui::FontLoader(encoding, resourceSystem->getVFS(), userDataPath, mScalingFactor));
         mFontLoader->loadBitmapFonts(exportFonts);
 
         //Register own widgets with MyGUI
@@ -1326,6 +1327,11 @@ namespace MWGui
         return mHud->getWorldMouseOver();
     }
 
+    float WindowManager::getScalingFactor()
+    {
+        return mScalingFactor;
+    }
+
     void WindowManager::executeInConsole (const std::string& path)
     {
         mConsole->executeFile (path);
diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp
index 3fd8b132f6..5a0d89ce10 100644
--- a/apps/openmw/mwgui/windowmanagerimp.hpp
+++ b/apps/openmw/mwgui/windowmanagerimp.hpp
@@ -208,6 +208,8 @@ namespace MWGui
     void setDragDrop(bool dragDrop) override;
     bool getWorldMouseOver() override;
 
+    float getScalingFactor() override;
+
     bool toggleFogOfWar() override;
     bool toggleFullHelp() override; ///< show extra info in item tooltips (owner, script)
     bool getFullHelp() const override;
@@ -518,6 +520,8 @@ namespace MWGui
 
     SDLUtil::VideoWrapper* mVideoWrapper;
 
+    float mScalingFactor;
+
     /**
      * Called when MyGUI tries to retrieve a tag's value. Tags must be denoted in #{tag} notation and will be replaced upon setting a user visible text/property.
      * Supported syntax:
diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp
index d17a4bd95d..03d492c9cf 100644
--- a/apps/openmw/mwinput/controllermanager.cpp
+++ b/apps/openmw/mwinput/controllermanager.cpp
@@ -32,7 +32,6 @@ namespace MWInput
         , mMouseManager(mouseManager)
         , mJoystickEnabled (Settings::Manager::getBool("enable controller", "Input"))
         , mGamepadCursorSpeed(Settings::Manager::getFloat("gamepad cursor speed", "Input"))
-        , mInvUiScalingFactor(1.f)
         , mSneakToggleShortcutTimer(0.f)
         , mGamepadZoom(0)
         , mGamepadGuiCursorEnabled(true)
@@ -69,10 +68,6 @@ namespace MWInput
             }
         }
 
-        float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
-        if (uiScale > 0.f)
-            mInvUiScalingFactor = 1.f / uiScale;
-
         float deadZoneRadius = Settings::Manager::getFloat("joystick dead zone", "Input");
         deadZoneRadius = std::min(std::max(deadZoneRadius, 0.0f), 0.5f);
         mBindingsManager->setJoystickDeadZone(deadZoneRadius);
@@ -102,8 +97,10 @@ namespace MWInput
 
             // We keep track of our own mouse position, so that moving the mouse while in
             // game mode does not move the position of the GUI cursor
-            float xMove = xAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed;
-            float yMove = yAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed;
+            float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
+            float xMove = xAxis * dt * 1500.0f / uiScale;
+            float yMove = yAxis * dt * 1500.0f / uiScale;
+
             float mouseWheelMove = -zAxis * dt * 1500.0f;
             if (xMove != 0 || yMove != 0 || mouseWheelMove != 0)
             {
diff --git a/apps/openmw/mwinput/controllermanager.hpp b/apps/openmw/mwinput/controllermanager.hpp
index 871f11102e..d8c62d57c4 100644
--- a/apps/openmw/mwinput/controllermanager.hpp
+++ b/apps/openmw/mwinput/controllermanager.hpp
@@ -52,7 +52,6 @@ namespace MWInput
 
         bool mJoystickEnabled;
         float mGamepadCursorSpeed;
-        float mInvUiScalingFactor;
         float mSneakToggleShortcutTimer;
         float mGamepadZoom;
         bool mGamepadGuiCursorEnabled;
diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp
index 8df116baa6..a696332dff 100644
--- a/apps/openmw/mwinput/mousemanager.cpp
+++ b/apps/openmw/mwinput/mousemanager.cpp
@@ -29,22 +29,18 @@ namespace MWInput
         , mCameraYMultiplier(Settings::Manager::getFloat("camera y multiplier", "Input"))
         , mBindingsManager(bindingsManager)
         , mInputWrapper(inputWrapper)
-        , mInvUiScalingFactor(1.f)
         , mGuiCursorX(0)
         , mGuiCursorY(0)
         , mMouseWheel(0)
         , mMouseLookEnabled(false)
         , mGuiCursorEnabled(true)
     {
-        float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
-        if (uiScale > 0.f)
-            mInvUiScalingFactor = 1.f / uiScale;
-
         int w,h;
         SDL_GetWindowSize(window, &w, &h);
 
-        mGuiCursorX = mInvUiScalingFactor * w / 2.f;
-        mGuiCursorY = mInvUiScalingFactor * h / 2.f;
+        float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
+        mGuiCursorX = w / (2.f * uiScale);
+        mGuiCursorY = h / (2.f * uiScale);
     }
 
     void MouseManager::processChangedSettings(const Settings::CategorySettingVector& changed)
@@ -79,8 +75,9 @@ namespace MWInput
 
             // We keep track of our own mouse position, so that moving the mouse while in
             // game mode does not move the position of the GUI cursor
-            mGuiCursorX = static_cast<float>(arg.x) * mInvUiScalingFactor;
-            mGuiCursorY = static_cast<float>(arg.y) * mInvUiScalingFactor;
+            float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
+            mGuiCursorX = static_cast<float>(arg.x) / uiScale;
+            mGuiCursorY = static_cast<float>(arg.y) / uiScale;
 
             mMouseWheel = static_cast<int>(arg.z);
 
@@ -249,6 +246,7 @@ namespace MWInput
 
     void MouseManager::warpMouse()
     {
-        mInputWrapper->warpMouse(static_cast<int>(mGuiCursorX / mInvUiScalingFactor), static_cast<int>(mGuiCursorY / mInvUiScalingFactor));
+        float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
+        mInputWrapper->warpMouse(static_cast<int>(mGuiCursorX*uiScale), static_cast<int>(mGuiCursorY*uiScale));
     }
 }
diff --git a/apps/openmw/mwinput/mousemanager.hpp b/apps/openmw/mwinput/mousemanager.hpp
index 3bf692bcf8..000e7cd0b6 100644
--- a/apps/openmw/mwinput/mousemanager.hpp
+++ b/apps/openmw/mwinput/mousemanager.hpp
@@ -47,7 +47,6 @@ namespace MWInput
 
         BindingsManager* mBindingsManager;
         SDLUtil::InputWrapper* mInputWrapper;
-        float mInvUiScalingFactor;
 
         float mGuiCursorX;
         float mGuiCursorY;
diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp
index 9a16232467..e395503a30 100644
--- a/apps/openmw/mwrender/localmap.cpp
+++ b/apps/openmw/mwrender/localmap.cpp
@@ -25,6 +25,7 @@
 #include <components/resource/resourcesystem.hpp>
 
 #include "../mwbase/environment.hpp"
+#include "../mwbase/windowmanager.hpp"
 #include "../mwbase/world.hpp"
 
 #include "../mwworld/cellstore.hpp"
@@ -92,9 +93,8 @@ LocalMap::LocalMap(osg::Group* root)
     , mInterior(false)
 {
     // Increase map resolution, if use UI scaling
-    float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
-    if (uiScale > 0.f)
-        mMapResolution *= uiScale;
+    float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
+    mMapResolution *= uiScale;
 
     SceneUtil::FindByNameVisitor find("Scene Root");
     mRoot->accept(find);
diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp
index 98fce32d2a..b4739110f7 100644
--- a/components/fontloader/fontloader.cpp
+++ b/components/fontloader/fontloader.cpp
@@ -146,10 +146,11 @@ namespace
 namespace Gui
 {
 
-    FontLoader::FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs, const std::string& userDataPath)
+    FontLoader::FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs, const std::string& userDataPath, float scalingFactor)
         : mVFS(vfs)
         , mUserDataPath(userDataPath)
         , mFontHeight(16)
+        , mScalingFactor(scalingFactor)
     {
         if (encoding == ToUTF8::WINDOWS_1252)
             mEncoding = ToUTF8::CP437;
@@ -566,11 +567,7 @@ namespace Gui
                 // to allow to configure font size via config file, without need to edit XML files.
                 // Also we should take UI scaling factor in account.
                 int resolution = Settings::Manager::getInt("ttf resolution", "GUI");
-                resolution = std::min(960, std::max(48, resolution));
-
-                float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
-                if (uiScale > 0.f)
-                    resolution *= uiScale;
+                resolution = std::min(960, std::max(48, resolution)) * mScalingFactor;
 
                 MyGUI::xml::ElementPtr resolutionNode = resourceNode->createChild("Property");
                 resolutionNode->addAttribute("key", "Resolution");
diff --git a/components/fontloader/fontloader.hpp b/components/fontloader/fontloader.hpp
index 94b0225016..8a46a0069e 100644
--- a/components/fontloader/fontloader.hpp
+++ b/components/fontloader/fontloader.hpp
@@ -27,7 +27,7 @@ namespace Gui
     class FontLoader
     {
     public:
-        FontLoader (ToUTF8::FromType encoding, const VFS::Manager* vfs, const std::string& userDataPath);
+        FontLoader (ToUTF8::FromType encoding, const VFS::Manager* vfs, const std::string& userDataPath, float scalingFactor);
         ~FontLoader();
 
         /// @param exportToFile export the converted fonts (Images and XML with glyph metrics) to files?
@@ -43,6 +43,7 @@ namespace Gui
         const VFS::Manager* mVFS;
         std::string mUserDataPath;
         int mFontHeight;
+        float mScalingFactor;
 
         std::vector<MyGUI::ITexture*> mTextures;
         std::vector<MyGUI::ResourceManualFont*> mFonts;
diff --git a/docs/source/reference/modding/settings/GUI.rst b/docs/source/reference/modding/settings/GUI.rst
index cad04ab5ce..00f99e47bc 100644
--- a/docs/source/reference/modding/settings/GUI.rst
+++ b/docs/source/reference/modding/settings/GUI.rst
@@ -5,12 +5,13 @@ scaling factor
 --------------
 
 :Type:		floating point
-:Range:		> 0.0
+:Range:		0.5 to 8.0
 :Default:	1.0
 
-This setting scales the GUI interface windows.
+This setting scales GUI windows.
 A value of 1.0 results in the normal scale. Larger values are useful to increase the scale of the GUI for high resolution displays.
-This setting can only be configured by editing the settings configuration file.
+
+This setting can be configured in the Interface section of Advanced tab of the launcher.
 
 font size
 ---------
diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui
index 03f54b7597..b6ec20634a 100644
--- a/files/ui/advancedpage.ui
+++ b/files/ui/advancedpage.ui
@@ -846,6 +846,55 @@ True: In non-combat mode camera is positioned behind the character's shoulder. C
            </property>
           </spacer>
          </item>
+         <item>
+          <layout class="QHBoxLayout" name="horizontalLayout_2"/>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_3">
+         <item>
+          <widget class="QLabel" name="scalingLabel">
+           <property name="toolTip">
+            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This setting scales GUI windows. A value of 1.0 results in the normal scale.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+           </property>
+           <property name="text">
+            <string>GUI scaling factor</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QDoubleSpinBox" name="scalingSpinBox">
+           <property name="decimals">
+            <number>2</number>
+           </property>
+           <property name="minimum">
+            <double>0.500000000000000</double>
+           </property>
+           <property name="maximum">
+            <double>8.000000000000000</double>
+           </property>
+           <property name="singleStep">
+            <double>0.250000000000000</double>
+           </property>
+           <property name="value">
+            <double>1.000000000000000</double>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer name="horizontalSpacer_5">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>40</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
         </layout>
        </item>
        <item>