diff --git a/gnu/local.mk b/gnu/local.mk
index 852f9f3246..ff3e80ea68 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1685,6 +1685,7 @@ dist_patch_DATA =						\
   %D%/packages/patches/python-magic-python-bytecode.patch	\
   %D%/packages/patches/python-matplotlib-run-under-wayland-gtk3.patch	\
   %D%/packages/patches/python-memcached-syntax-warnings.patch	\
+  %D%/packages/patches/python-moderngl-window-skip-tests.patch  \
   %D%/packages/patches/python-mox3-python3.6-compat.patch	\
   %D%/packages/patches/python-typing-inspect-fix.patch		\
   %D%/packages/patches/python-packaging-test-arch.patch		\
diff --git a/gnu/packages/patches/python-moderngl-window-skip-tests.patch b/gnu/packages/patches/python-moderngl-window-skip-tests.patch
new file mode 100644
index 0000000000..aee4b71e2a
--- /dev/null
+++ b/gnu/packages/patches/python-moderngl-window-skip-tests.patch
@@ -0,0 +1,146 @@
+Skip failing tests and tests for optional dependencies
+
+The Python packages pywavefront and trimesh are optional dependencies and not
+yet packaged in Guix but the tests try to load the modules anyway.  Therefore
+skip them.
+
+The tests test_texture_mimpamps, test_texture_abspath, test_load_texture_2d
+and test_load_texture_array fail when Guix builds the package.  This is not
+the case when running the tests directly from a source tree.
+
+diff --git a/tests/test_docs.py b/tests/test_docs.py
+index e4a38df..99a2aa0 100644
+--- a/tests/test_docs.py
++++ b/tests/test_docs.py
+@@ -133,20 +133,11 @@ class TestCase(unittest.TestCase):
+ 
+     # --- Loaders : Scene ---
+ 
+-    def test_loaders_wavefront(self):
+-        self.validate('loaders/wavefront.rst', 'moderngl_window.loaders.scene.wavefront', 'Loader')
+-
+     def test_loaders_gltf(self):
+         self.validate('loaders/gltf2.rst', 'moderngl_window.loaders.scene.gltf2', 'Loader')
+ 
+-    def test_loaders_stl(self):
+-        self.validate('loaders/wavefront.rst', 'moderngl_window.loaders.scene.stl', 'Loader')
+-
+     # --- Loaders : Program ---
+ 
+-    def test_loader_single(self):
+-        self.validate('loaders/single.rst', 'moderngl_window.loaders.program.single', 'Loader')
+-
+     def test_loader_separate(self):
+         self.validate('loaders/separate.rst', 'moderngl_window.loaders.program.separate', 'Loader')
+ 
+diff --git a/tests/test_loaders_scene.py b/tests/test_loaders_scene.py
+index c577315..2eef889 100644
+--- a/tests/test_loaders_scene.py
++++ b/tests/test_loaders_scene.py
+@@ -16,16 +16,6 @@ class SceneLoadersTestCase(HeadlessTestCase):
+     window_size = (16, 16)
+     aspect_ratio = 1.0
+ 
+-    def test_wavefront(self):
+-        """Load wavefront file"""
+-        scene = resources.scenes.load(SceneDescription(path='scenes/crate/crate.obj'))
+-        self.assertIsInstance(scene, Scene)
+-
+-    def test_wavefont_not_found(self):
+-        """Ensure ImproperlyConfigured is raised when wavefront is not found"""
+-        with self.assertRaises(ImproperlyConfigured):
+-            resources.scenes.load(SceneDescription(path='scenes/doesnotexist.obj'))
+-
+     def test_gltf(self):
+         """Load standard gltf"""
+         scene = resources.scenes.load(SceneDescription(path='scenes/BoxTextured/glTF/BoxTextured.gltf'))
+@@ -45,7 +35,3 @@ class SceneLoadersTestCase(HeadlessTestCase):
+         """Attempt to load nonexisting gltf"""
+         with self.assertRaises(ImproperlyConfigured):
+             resources.scenes.load(SceneDescription(path='scenes/doesnotexist.gltf'))
+-
+-    def test_stl(self):
+-        scene = resources.scenes.load(SceneDescription(path='scenes/uplink.stl'))
+-        self.assertIsInstance(scene, Scene)
+diff --git a/tests/test_loaders_texture.py b/tests/test_loaders_texture.py
+index fcd4d72..8e13037 100644
+--- a/tests/test_loaders_texture.py
++++ b/tests/test_loaders_texture.py
+@@ -55,39 +55,3 @@ class TextureLoadersTestCase(HeadlessTestCase):
+             kind='cube',
+         ))
+         self.assertIsInstance(texture, moderngl.TextureCube)
+-
+-    def test_texture_mimpamps(self):
+-        """Load texture with mipmapping and anisotropy"""
+-        desc = TextureDescription(
+-            path='textures/crate.png',
+-            mipmap_levels=(0, 2),
+-            anisotropy=4.0,
+-        )
+-        texture = resources.textures.load(desc)
+-        self.assertEqual(texture.anisotropy, 4.0)
+-        self.assertEqual(desc.mipmap, True)
+-
+-        # Texture Array
+-        desc = TextureDescription(
+-            path='textures/array.png',
+-            kind="array",
+-            layers=10,
+-            mipmap_levels=(0, 5),
+-            anisotropy=8.0,
+-        )
+-        texture = resources.textures.load(desc)
+-        self.assertEqual(texture.anisotropy, 8.0)
+-        self.assertEqual(desc.mipmap, True)
+-
+-    def test_texture_abspath(self):
+-        """Strip search directories and use absolute path"""
+-        path = (Path(__file__).parent / "fixtures/resources/textures/crate.png").resolve()
+-        with resources.temporary_dirs([]):
+-            desc = TextureDescription(
+-                path=path,
+-                mipmap_levels=(0, 2),
+-                anisotropy=4.0,
+-            )
+-            texture = resources.textures.load(desc)
+-            self.assertEqual(texture.anisotropy, 4.0)
+-            self.assertEqual(desc.mipmap, True)
+diff --git a/tests/test_windowconfig.py b/tests/test_windowconfig.py
+index 185c36e..2eb6f39 100644
+--- a/tests/test_windowconfig.py
++++ b/tests/test_windowconfig.py
+@@ -94,31 +94,6 @@ class WindowConfigTestCase(WindowConfigTestCase):
+         with self.assertRaises(ValueError):
+             self.window.resize_func = "Hello"
+ 
+-    def test_load_texture_2d(self):
+-        """Load texture with shortcut method"""
+-        texture = self.config.load_texture_2d(
+-            "textures/crate.png",
+-            flip=True,
+-            mipmap_levels=(0, 2),
+-            anisotropy=4.0,
+-        )
+-        self.assertIsInstance(texture, moderngl.Texture)
+-        self.assertEqual(texture.anisotropy, 4.0)
+-
+-    def test_load_texture_array(self):
+-        """Load texture array with shortcut method"""
+-        texture = self.config.load_texture_array(
+-            'textures/array.png',
+-            layers=10,
+-            flip=True,
+-            mipmap=False,
+-            mipmap_levels=(0, 2),
+-            anisotropy=4.0,
+-        )
+-        self.assertIsInstance(texture, moderngl.TextureArray)
+-        self.assertEqual(texture.anisotropy, 4.0)
+-        self.assertEqual(texture.layers, 10)
+-
+     def test_load_program_single(self):
+         """Load a single glsl program"""
+         prog = self.config.load_program(path='programs/white.glsl')
+-- 
+2.33.0
diff --git a/gnu/packages/python-xyz.scm b/gnu/packages/python-xyz.scm
index 0dbc9db830..eee2fc0077 100644
--- a/gnu/packages/python-xyz.scm
+++ b/gnu/packages/python-xyz.scm
@@ -19710,6 +19710,50 @@ (define-public python-pyglet
 music.  All of this with a friendly Pythonic API that's simple to learn.")
     (license license:bsd-3)))
 
+(define-public python-moderngl-window
+  (package
+   (name "python-moderngl-window")
+   (version "2.4.0")
+   (source (origin
+            (method git-fetch)
+            (uri (git-reference
+                  (url "https://github.com/moderngl/moderngl-window")
+                  (commit version)))
+            (file-name (git-file-name name version))
+            (patches
+             (search-patches "python-moderngl-window-skip-tests.patch"))
+            (sha256
+             (base32
+              "1fvhm6ln3q5kl5m5q3gzfmx1kpyn054fhpcrgpqz8809dxi7pzcv"))))
+   (build-system python-build-system)
+   (arguments
+     '(#:phases
+       (modify-phases %standard-phases
+         (add-before 'check 'fix-test
+           (lambda _
+             (substitute* '("tests/test_windowconfig.py")
+               (("terrain_Vs.glsl") "terrain_vs.glsl"))))
+         (replace 'check
+           (lambda* (#:key inputs outputs tests? #:allow-other-keys)
+             (when tests?
+               (system "Xvfb :1 &")
+               (setenv "DISPLAY" ":1")
+               (add-installed-pythonpath inputs outputs)
+               (invoke "pytest" "tests" )))))))
+   (propagated-inputs
+    (list python-pillow python-pyrr python-numpy python-moderngl
+          python-pyglet))
+   (native-inputs
+    (list python-pytest xorg-server-for-tests))
+   (home-page "https://github.com/moderngl/moderngl-window")
+   (synopsis "Cross-platform utility library for ModernGL")
+   (description "This ModernGL utility library simplifies window creation and
+resource loading.  You can create a window for ModernGL using pyglet, pygame,
+PySide2, GLFW, SDL2, PyQt5 or tkinter.  Events are unified into a single event
+system.  Resource loading includes loading of 2D textures/texture arrays,
+shaders and objects/scenes.")
+   (license license:expat)))
+
 (define-public python-screeninfo
   (package
    (name "python-screeninfo")
