diff --git a/crates/eframe/src/epi/mod.rs b/crates/eframe/src/epi/mod.rs
index d45b39cfbdf2..508c1c5545b7 100644
--- a/crates/eframe/src/epi/mod.rs
+++ b/crates/eframe/src/epi/mod.rs
@@ -28,7 +28,7 @@ use static_assertions::assert_not_impl_any;
 
 #[cfg(not(target_arch = "wasm32"))]
 #[cfg(any(feature = "glow", feature = "wgpu"))]
-pub use winit::event_loop::EventLoopBuilder;
+pub use winit::{event_loop::EventLoopBuilder, window::WindowBuilder};
 
 /// Hook into the building of an event loop before it is run
 ///
@@ -38,6 +38,14 @@ pub use winit::event_loop::EventLoopBuilder;
 #[cfg(any(feature = "glow", feature = "wgpu"))]
 pub type EventLoopBuilderHook = Box<dyn FnOnce(&mut EventLoopBuilder<UserEvent>)>;
 
+/// Hook into the building of a the native window.
+///
+/// You can configure any platform specific details required on top of the default configuration
+/// done by `eframe`.
+#[cfg(not(target_arch = "wasm32"))]
+#[cfg(any(feature = "glow", feature = "wgpu"))]
+pub type WindowBuilderHook = Box<dyn FnOnce(WindowBuilder) -> WindowBuilder>;
+
 /// This is how your app is created.
 ///
 /// You can use the [`CreationContext`] to setup egui, restore state, setup OpenGL things, etc.
@@ -397,6 +405,15 @@ pub struct NativeOptions {
     #[cfg(any(feature = "glow", feature = "wgpu"))]
     pub event_loop_builder: Option<EventLoopBuilderHook>,
 
+    /// Hook into the building of a window.
+    ///
+    /// Specify a callback here in case you need to make platform specific changes to the
+    /// window appearance.
+    ///
+    /// Note: A [`NativeOptions`] clone will not include any `window_builder` hook.
+    #[cfg(any(feature = "glow", feature = "wgpu"))]
+    pub window_builder: Option<WindowBuilderHook>,
+
     #[cfg(feature = "glow")]
     /// Needed for cross compiling for VirtualBox VMSVGA driver with OpenGL ES 2.0 and OpenGL 2.1 which doesn't support SRGB texture.
     /// See <https://github.com/emilk/egui/pull/1993>.
@@ -466,6 +483,9 @@ impl Clone for NativeOptions {
             #[cfg(any(feature = "glow", feature = "wgpu"))]
             event_loop_builder: None, // Skip any builder callbacks if cloning
 
+            #[cfg(any(feature = "glow", feature = "wgpu"))]
+            window_builder: None, // Skip any builder callbacks if cloning
+
             #[cfg(feature = "wgpu")]
             wgpu_options: self.wgpu_options.clone(),
 
@@ -520,6 +540,9 @@ impl Default for NativeOptions {
             #[cfg(any(feature = "glow", feature = "wgpu"))]
             event_loop_builder: None,
 
+            #[cfg(any(feature = "glow", feature = "wgpu"))]
+            window_builder: None,
+
             #[cfg(feature = "glow")]
             shader_version: None,
 
diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs
index 3912410d8bf2..e4e1a3f99cfb 100644
--- a/crates/eframe/src/native/epi_integration.rs
+++ b/crates/eframe/src/native/epi_integration.rs
@@ -75,7 +75,7 @@ pub fn read_window_info(
 pub fn window_builder<E>(
     event_loop: &EventLoopWindowTarget<E>,
     title: &str,
-    native_options: &epi::NativeOptions,
+    native_options: &mut epi::NativeOptions,
     window_settings: Option<WindowSettings>,
 ) -> winit::window::WindowBuilder {
     let epi::NativeOptions {
@@ -179,7 +179,10 @@ pub fn window_builder<E>(
         }
     }
 
-    window_builder
+    match std::mem::take(&mut native_options.window_builder) {
+        Some(hook) => hook(window_builder),
+        None => window_builder,
+    }
 }
 
 pub fn apply_native_options_to_window(
diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs
index 7631cd784864..ff6aff70ca64 100644
--- a/crates/eframe/src/native/run.rs
+++ b/crates/eframe/src/native/run.rs
@@ -702,7 +702,7 @@ mod glow_integration {
             event_loop: &EventLoopWindowTarget<UserEvent>,
             storage: Option<&dyn epi::Storage>,
             title: &str,
-            native_options: &NativeOptions,
+            native_options: &mut NativeOptions,
         ) -> Result<(GlutinWindowContext, glow::Context)> {
             crate::profile_function!();
 
@@ -749,7 +749,7 @@ mod glow_integration {
                 event_loop,
                 storage.as_deref(),
                 &self.app_name,
-                &self.native_options,
+                &mut self.native_options,
             )?;
             let gl = Arc::new(gl);
 
@@ -1184,7 +1184,7 @@ mod wgpu_integration {
             event_loop: &EventLoopWindowTarget<UserEvent>,
             storage: Option<&dyn epi::Storage>,
             title: &str,
-            native_options: &NativeOptions,
+            native_options: &mut NativeOptions,
         ) -> std::result::Result<winit::window::Window, winit::error::OsError> {
             crate::profile_function!();
 
@@ -1439,7 +1439,7 @@ mod wgpu_integration {
                                 event_loop,
                                 running.integration.frame.storage(),
                                 &self.app_name,
-                                &self.native_options,
+                                &mut self.native_options,
                             )?;
                             self.set_window(window)?;
                         }
@@ -1454,7 +1454,7 @@ mod wgpu_integration {
                             event_loop,
                             storage.as_deref(),
                             &self.app_name,
-                            &self.native_options,
+                            &mut self.native_options,
                         )?;
                         self.init_run_state(event_loop, storage, window)?;
                     }