diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 744276fb0a..a9af986914 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,6 +14,7 @@ env:
 # - local: build for the same target as we compile on, and do local tests
 # - other: build without testing, e.g. cross-build
 # - web: build for the Web
+# - em: build for the Emscripten
 
 jobs:
   build:
@@ -94,6 +95,12 @@ jobs:
             tool: clippy
             kind: web
 
+          - name: Emscripten
+            os: ubuntu-20.04
+            target: wasm32-unknown-emscripten
+            tool: clippy
+            kind: em
+
     name: Check ${{ matrix.name }}
     runs-on: ${{ matrix.os }}
 
@@ -168,6 +175,15 @@ jobs:
           # build docs
           cargo doc --target ${{ matrix.target }} -p wgpu --no-deps
 
+      - name: check em
+        if: matrix.kind == 'em'
+        run: |
+          # build for Emscripten/WebGL
+          cargo ${{matrix.tool}} --target ${{ matrix.target }} -p wgpu -p wgpu-hal --features webgl,emscripten
+
+          # build raw-gles example
+          cargo ${{matrix.tool}} --target ${{ matrix.target }} --example raw-gles --features webgl,emscripten
+
       - name: check native
         if: matrix.kind == 'local' || matrix.kind == 'other'
         run: |
diff --git a/Cargo.lock b/Cargo.lock
index b46a6bfbaa..807a7fafad 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -900,6 +900,7 @@ checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3"
 dependencies = [
  "libc",
  "libloading",
+ "pkg-config",
 ]
 
 [[package]]
diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml
index fecdbec031..53ec9ef4e3 100644
--- a/wgpu-core/Cargo.toml
+++ b/wgpu-core/Cargo.toml
@@ -65,5 +65,8 @@ hal = { path = "../wgpu-hal", package = "wgpu-hal", version = "0.12", features =
 [target.'cfg(all(not(target_arch = "wasm32"), windows))'.dependencies]
 hal = { path = "../wgpu-hal", package = "wgpu-hal", version = "0.12", features = ["vulkan", "dx12", "renderdoc"] }
 
+[target.'cfg(target_os = "emscripten")'.dependencies]
+hal = { path = "../wgpu-hal", package = "wgpu-hal", version = "0.12", features = ["emscripten"] }
+
 [build-dependencies]
 cfg_aliases = "0.1"
diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml
index 79b1a39175..5bd4823112 100644
--- a/wgpu-hal/Cargo.toml
+++ b/wgpu-hal/Cargo.toml
@@ -18,6 +18,7 @@ vulkan = ["naga/spv-out", "ash", "gpu-alloc", "gpu-descriptor", "libloading", "i
 gles = ["naga/glsl-out", "glow", "egl", "libloading"]
 dx12 = ["naga/hlsl-out", "native", "bit-set", "range-alloc", "winapi/d3d12", "winapi/d3d12shader", "winapi/d3d12sdklayers", "winapi/dxgi1_6"]
 renderdoc = ["libloading", "renderdoc-sys"]
+emscripten = ["gles"]
 
 [[example]]
 name = "halmark"
@@ -66,6 +67,11 @@ egl = { package = "khronos-egl", version = "4.1", features = ["dynamic"], option
 #Note: it's only unused on Apple platforms
 libloading = { version = "0.7", optional = true }
 
+[target.'cfg(target_os = "emscripten")'.dependencies]
+egl = { package = "khronos-egl", version = "4.1", features = ["static", "no-pkg-config"] }
+#Note: it's unused by emscripten, but we keep it to have single code base in egl.rs
+libloading = { version = "0.7", optional = true }
+
 [target.'cfg(windows)'.dependencies]
 winapi = { version = "0.3", features = ["libloaderapi", "windef", "winuser"] }
 native = { package = "d3d12", version = "0.4.1", features = ["libloading"], optional = true }
@@ -75,7 +81,7 @@ mtl = { package = "metal", git = "https://github.com/gfx-rs/metal-rs", rev = "14
 objc = "0.2.5"
 core-graphics-types = "0.1"
 
-[target.'cfg(target_arch = "wasm32")'.dependencies]
+[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies]
 wasm-bindgen = { version = "0.2" }
 web-sys = { version = "0.3", features = ["Window", "HtmlCanvasElement", "WebGl2RenderingContext"] }
 js-sys = { version = "0.3" }
diff --git a/wgpu-hal/examples/raw-gles.em.html b/wgpu-hal/examples/raw-gles.em.html
new file mode 100644
index 0000000000..f3587e8575
--- /dev/null
+++ b/wgpu-hal/examples/raw-gles.em.html
@@ -0,0 +1,16 @@
+<html>
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  </head>
+  <body>
+    <canvas id="canvas" width="640" height="400"></canvas>
+    <script>
+        var Module = {
+            canvas: document.getElementById("canvas"),
+            preRun: [function() {ENV.RUST_LOG = "debug"}]
+        };
+    </script>
+    <script src="raw-gles.js"></script>
+  </body>
+</html>
\ No newline at end of file
diff --git a/wgpu-hal/examples/raw-gles.rs b/wgpu-hal/examples/raw-gles.rs
index 23cb6df431..815f4c1829 100644
--- a/wgpu-hal/examples/raw-gles.rs
+++ b/wgpu-hal/examples/raw-gles.rs
@@ -1,15 +1,16 @@
 //! This example shows interop with raw GLES contexts -
 //! the ability to hook up wgpu-hal to an existing context and draw into it.
+//!
+//! Emscripten build:
+//! 1. install emsdk
+//! 2. build this example with cargo:
+//!    EMMAKEN_CFLAGS="-g -s ERROR_ON_UNDEFINED_SYMBOLS=0 --no-entry -s FULL_ES3=1" cargo build --example raw-gles --target wasm32-unknown-emscripten --features emscripten,webgl
+//! 3. copy raw-gles.em.html into target/wasm32-unknown-emscripten/debug/examples/ and open it in browser
 
 extern crate wgpu_hal as hal;
 
-#[cfg(target_arch = "wasm32")]
-fn main() {}
-
 #[cfg(not(target_arch = "wasm32"))]
 fn main() {
-    use hal::{Adapter as _, CommandEncoder as _, Device as _, Queue as _};
-
     env_logger::init();
     println!("Initializing external GL context");
 
@@ -32,6 +33,94 @@ fn main() {
         })
     }
     .expect("GL adapter can't be initialized");
+
+    fill_screen(&exposed, inner_size.width, inner_size.height);
+
+    println!("Showing the window");
+    gl_context.swap_buffers().unwrap();
+
+    event_loop.run(move |event, _, control_flow| {
+        use glutin::{
+            event::{Event, KeyboardInput, VirtualKeyCode, WindowEvent},
+            event_loop::ControlFlow,
+        };
+        *control_flow = ControlFlow::Wait;
+
+        match event {
+            Event::LoopDestroyed => return,
+            Event::WindowEvent { event, .. } => match event {
+                WindowEvent::CloseRequested
+                | WindowEvent::KeyboardInput {
+                    input:
+                        KeyboardInput {
+                            virtual_keycode: Some(VirtualKeyCode::Escape),
+                            ..
+                        },
+                    ..
+                } => *control_flow = ControlFlow::Exit,
+                _ => (),
+            },
+            _ => (),
+        }
+    });
+}
+
+#[cfg(feature = "emscripten")]
+fn main() {
+    env_logger::init();
+
+    println!("Initializing external GL context");
+    let egl = egl::Instance::new(egl::Static);
+    let display = egl.get_display(egl::DEFAULT_DISPLAY).unwrap();
+    egl.initialize(display)
+        .expect("unable to initialize display");
+
+    let attributes = [
+        egl::RED_SIZE,
+        8,
+        egl::GREEN_SIZE,
+        8,
+        egl::BLUE_SIZE,
+        8,
+        egl::NONE,
+    ];
+
+    let config = egl
+        .choose_first_config(display, &attributes)
+        .unwrap()
+        .expect("unable to choose config");
+    let surface = unsafe {
+        let window = std::ptr::null_mut::<std::ffi::c_void>();
+        egl.create_window_surface(display, config, window, None)
+    }
+    .expect("unable to create surface");
+
+    let context_attributes = [egl::CONTEXT_CLIENT_VERSION, 3, egl::NONE];
+
+    let gl_context = egl
+        .create_context(display, config, None, &context_attributes)
+        .expect("unable to create context");
+    egl.make_current(display, Some(surface), Some(surface), Some(gl_context))
+        .expect("can't make context current");
+
+    println!("Hooking up to wgpu-hal");
+    let exposed = unsafe {
+        <hal::api::Gles as hal::Api>::Adapter::new_external(|name| {
+            egl.get_proc_address(name)
+                .map_or(std::ptr::null(), |p| p as *const _)
+        })
+    }
+    .expect("GL adapter can't be initialized");
+
+    fill_screen(&exposed, 640, 400);
+}
+
+#[cfg(all(target_arch = "wasm32", not(feature = "emscripten")))]
+fn main() {}
+
+fn fill_screen(exposed: &hal::ExposedAdapter<hal::api::Gles>, width: u32, height: u32) {
+    use hal::{Adapter as _, CommandEncoder as _, Device as _, Queue as _};
+
     let mut od = unsafe {
         exposed
             .adapter
@@ -68,8 +157,8 @@ fn main() {
     let rp_desc = hal::RenderPassDescriptor {
         label: None,
         extent: wgt::Extent3d {
-            width: inner_size.width,
-            height: inner_size.height,
+            width,
+            height,
             depth_or_array_layers: 1,
         },
         sample_count: 1,
@@ -92,32 +181,4 @@ fn main() {
         let cmd_buf = encoder.end_encoding().unwrap();
         od.queue.submit(&[&cmd_buf], None).unwrap();
     }
-
-    println!("Showing the window");
-    gl_context.swap_buffers().unwrap();
-
-    event_loop.run(move |event, _, control_flow| {
-        use glutin::{
-            event::{Event, KeyboardInput, VirtualKeyCode, WindowEvent},
-            event_loop::ControlFlow,
-        };
-        *control_flow = ControlFlow::Wait;
-
-        match event {
-            Event::LoopDestroyed => return,
-            Event::WindowEvent { event, .. } => match event {
-                WindowEvent::CloseRequested
-                | WindowEvent::KeyboardInput {
-                    input:
-                        KeyboardInput {
-                            virtual_keycode: Some(VirtualKeyCode::Escape),
-                            ..
-                        },
-                    ..
-                } => *control_flow = ControlFlow::Exit,
-                _ => (),
-            },
-            _ => (),
-        }
-    });
 }
diff --git a/wgpu-hal/src/gles/egl.rs b/wgpu-hal/src/gles/egl.rs
index ea49dfb3e6..6ff7d61fc9 100644
--- a/wgpu-hal/src/gles/egl.rs
+++ b/wgpu-hal/src/gles/egl.rs
@@ -26,6 +26,12 @@ type WlDisplayConnectFun =
 
 type WlDisplayDisconnectFun = unsafe extern "system" fn(display: *const raw::c_void);
 
+#[cfg(not(feature = "emscripten"))]
+type EglInstance = egl::DynamicInstance<egl::EGL1_4>;
+
+#[cfg(feature = "emscripten")]
+type EglInstance = egl::Instance<egl::Static>;
+
 type WlEglWindowCreateFun = unsafe extern "system" fn(
     surface: *const raw::c_void,
     width: raw::c_int,
@@ -154,7 +160,7 @@ enum SrgbFrameBufferKind {
 
 /// Choose GLES framebuffer configuration.
 fn choose_config(
-    egl: &egl::DynamicInstance<egl::EGL1_4>,
+    egl: &EglInstance,
     display: egl::Display,
     srgb_kind: SrgbFrameBufferKind,
 ) -> Result<(egl::Config, bool), crate::InstanceError> {
@@ -263,7 +269,7 @@ fn gl_debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, m
 
 #[derive(Clone, Debug)]
 struct EglContext {
-    instance: Arc<egl::DynamicInstance<egl::EGL1_4>>,
+    instance: Arc<EglInstance>,
     display: egl::Display,
     raw: egl::Context,
     pbuffer: Option<egl::Surface>,
@@ -307,7 +313,7 @@ impl AdapterContext {
 }
 
 struct EglContextLock<'a> {
-    instance: &'a Arc<egl::DynamicInstance<egl::EGL1_4>>,
+    instance: &'a Arc<EglInstance>,
     display: egl::Display,
 }
 
@@ -385,6 +391,7 @@ struct Inner {
     version: (i32, i32),
     supports_native_window: bool,
     config: egl::Config,
+    #[cfg_attr(feature = "emscripten", allow(dead_code))]
     wl_display: Option<*mut raw::c_void>,
     /// Method by which the framebuffer should support srgb
     srgb_kind: SrgbFrameBufferKind,
@@ -393,7 +400,7 @@ struct Inner {
 impl Inner {
     fn create(
         flags: crate::InstanceFlags,
-        egl: Arc<egl::DynamicInstance<egl::EGL1_4>>,
+        egl: Arc<EglInstance>,
         display: egl::Display,
     ) -> Result<Self, crate::InstanceError> {
         let version = egl.initialize(display).map_err(|_| crate::InstanceError)?;
@@ -564,6 +571,10 @@ unsafe impl Sync for Instance {}
 
 impl crate::Instance<super::Api> for Instance {
     unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
+        #[cfg(feature = "emscripten")]
+        let egl_result: Result<EglInstance, egl::Error> = Ok(egl::Instance::new(egl::Static));
+
+        #[cfg(not(feature = "emscripten"))]
         let egl_result = if cfg!(windows) {
             egl::DynamicInstance::<egl::EGL1_4>::load_required_from_filename("libEGL.dll")
         } else if cfg!(any(target_os = "macos", target_os = "ios")) {
@@ -606,8 +617,14 @@ impl crate::Instance<super::Api> for Instance {
             None
         };
 
+        #[cfg(not(feature = "emscripten"))]
+        let egl1_5 = egl.upcast::<egl::EGL1_5>();
+
+        #[cfg(feature = "emscripten")]
+        let egl1_5: Option<&Arc<EglInstance>> = Some(&egl);
+
         let (display, wsi_library, wsi_kind) = if let (Some(library), Some(egl)) =
-            (wayland_library, egl.upcast::<egl::EGL1_5>())
+            (wayland_library, egl1_5)
         {
             log::info!("Using Wayland platform");
             let display_attributes = [egl::ATTRIB_NONE];
@@ -619,18 +636,14 @@ impl crate::Instance<super::Api> for Instance {
                 )
                 .unwrap();
             (display, Some(Arc::new(library)), WindowKind::Wayland)
-        } else if let (Some((display, library)), Some(egl)) =
-            (x11_display_library, egl.upcast::<egl::EGL1_5>())
-        {
+        } else if let (Some((display, library)), Some(egl)) = (x11_display_library, egl1_5) {
             log::info!("Using X11 platform");
             let display_attributes = [egl::ATTRIB_NONE];
             let display = egl
                 .get_platform_display(EGL_PLATFORM_X11_KHR, display.as_ptr(), &display_attributes)
                 .unwrap();
             (display, Some(Arc::new(library)), WindowKind::X11)
-        } else if let (Some((display, library)), Some(egl)) =
-            (angle_x11_display_library, egl.upcast::<egl::EGL1_5>())
-        {
+        } else if let (Some((display, library)), Some(egl)) = (angle_x11_display_library, egl1_5) {
             log::info!("Using Angle platform with X11");
             let display_attributes = [
                 EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE as egl::Attrib,
@@ -698,7 +711,7 @@ impl crate::Instance<super::Api> for Instance {
 
         let raw_window_handle = has_handle.raw_window_handle();
 
-        #[cfg_attr(target_os = "android", allow(unused_mut))]
+        #[cfg_attr(any(target_os = "android", feature = "emscripten"), allow(unused_mut))]
         let mut inner = self.inner.lock();
 
         match raw_window_handle {
@@ -721,6 +734,7 @@ impl crate::Instance<super::Api> for Instance {
                     return Err(crate::InstanceError);
                 }
             }
+            #[cfg(not(feature = "emscripten"))]
             Rwh::Wayland(handle) => {
                 /* Wayland displays are not sharable between surfaces so if the
                  * surface we receive from this handle is from a different
@@ -757,6 +771,8 @@ impl crate::Instance<super::Api> for Instance {
                     drop(old_inner);
                 }
             }
+            #[cfg(feature = "emscripten")]
+            Rwh::Web(_) => {}
             other => {
                 log::error!("Unsupported window: {:?}", other);
                 return Err(crate::InstanceError);
@@ -975,6 +991,8 @@ impl crate::Surface<super::Api> for Surface {
                         wl_window = Some(window);
                         window
                     }
+                    #[cfg(feature = "emscripten")]
+                    (WindowKind::Unknown, Rwh::Web(handle)) => handle.id as *mut std::ffi::c_void,
                     (WindowKind::Unknown, Rwh::Win32(handle)) => handle.hwnd,
                     (WindowKind::Unknown, Rwh::AppKit(handle)) => handle.ns_view,
                     _ => {
@@ -1014,8 +1032,14 @@ impl crate::Surface<super::Api> for Surface {
                 }
                 attributes.push(egl::ATTRIB_NONE as i32);
 
+                #[cfg(not(feature = "emscripten"))]
+                let egl1_5 = self.egl.instance.upcast::<egl::EGL1_5>();
+
+                #[cfg(feature = "emscripten")]
+                let egl1_5: Option<&Arc<EglInstance>> = Some(&self.egl.instance);
+
                 // Careful, we can still be in 1.4 version even if `upcast` succeeds
-                let raw_result = match self.egl.instance.upcast::<egl::EGL1_5>() {
+                let raw_result = match egl1_5 {
                     Some(egl) if self.wsi.kind != WindowKind::Unknown => {
                         let attributes_usize = attributes
                             .into_iter()
diff --git a/wgpu-hal/src/gles/mod.rs b/wgpu-hal/src/gles/mod.rs
index 7a89b2bbed..c48225f6d6 100644
--- a/wgpu-hal/src/gles/mod.rs
+++ b/wgpu-hal/src/gles/mod.rs
@@ -56,9 +56,9 @@ To address this, we invalidate the vertex buffers based on:
 
 */
 
-#[cfg(not(target_arch = "wasm32"))]
+#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
 mod egl;
-#[cfg(target_arch = "wasm32")]
+#[cfg(all(target_arch = "wasm32", not(feature = "emscripten")))]
 mod web;
 
 mod adapter;
@@ -67,10 +67,10 @@ mod conv;
 mod device;
 mod queue;
 
-#[cfg(not(target_arch = "wasm32"))]
+#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
 use self::egl::{AdapterContext, Instance, Surface};
 
-#[cfg(target_arch = "wasm32")]
+#[cfg(all(target_arch = "wasm32", not(feature = "emscripten")))]
 use self::web::{AdapterContext, Instance, Surface};
 
 use arrayvec::ArrayVec;
diff --git a/wgpu-hal/src/gles/queue.rs b/wgpu-hal/src/gles/queue.rs
index 731ff99185..e6f9f549f6 100644
--- a/wgpu-hal/src/gles/queue.rs
+++ b/wgpu-hal/src/gles/queue.rs
@@ -1176,10 +1176,10 @@ impl crate::Queue<super::Api> for super::Queue {
         surface: &mut super::Surface,
         texture: super::Texture,
     ) -> Result<(), crate::SurfaceError> {
-        #[cfg(not(target_arch = "wasm32"))]
+        #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
         let gl = &self.shared.context.get_without_egl_lock();
 
-        #[cfg(target_arch = "wasm32")]
+        #[cfg(all(target_arch = "wasm32", not(feature = "emscripten")))]
         let gl = &self.shared.context.glow_context;
 
         surface.present(texture, gl)
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml
index fe69c54961..f7564e4a4b 100644
--- a/wgpu/Cargo.toml
+++ b/wgpu/Cargo.toml
@@ -83,6 +83,7 @@ trace = ["serde", "wgc/trace"]
 replay = ["serde", "wgc/replay"]
 angle = ["wgc/angle"]
 webgl = ["wgc"]
+emscripten = ["webgl"]
 
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies.wgc]
 package = "wgpu-core"
@@ -102,7 +103,7 @@ package = "wgpu-types"
 path = "../wgpu-types"
 version = "0.12"
 
-[target.'cfg(not(target_arch = "wasm32"))'.dependencies.hal]
+[target.'cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))'.dependencies.hal]
 package = "wgpu-hal"
 path = "../wgpu-hal"
 version = "0.12"
diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs
index aa1c990d1d..f7a16e1237 100644
--- a/wgpu/src/backend/direct.rs
+++ b/wgpu/src/backend/direct.rs
@@ -38,7 +38,7 @@ impl fmt::Debug for Context {
 }
 
 impl Context {
-    #[cfg(not(target_arch = "wasm32"))]
+    #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
     pub unsafe fn from_hal_instance<A: wgc::hub::HalApi>(hal_instance: A::Instance) -> Self {
         Self(wgc::hub::Global::from_hal_instance::<A>(
             "wgpu",
@@ -51,7 +51,7 @@ impl Context {
         &self.0
     }
 
-    #[cfg(not(target_arch = "wasm32"))]
+    #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
     pub fn enumerate_adapters(&self, backends: wgt::Backends) -> Vec<wgc::id::AdapterId> {
         self.0
             .enumerate_adapters(wgc::instance::AdapterInputs::Mask(backends, |_| {
@@ -59,7 +59,7 @@ impl Context {
             }))
     }
 
-    #[cfg(not(target_arch = "wasm32"))]
+    #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
     pub unsafe fn create_adapter_from_hal<A: wgc::hub::HalApi>(
         &self,
         hal_adapter: hal::ExposedAdapter<A>,
@@ -67,7 +67,7 @@ impl Context {
         self.0.create_adapter_from_hal(hal_adapter, PhantomData)
     }
 
-    #[cfg(not(target_arch = "wasm32"))]
+    #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
     pub unsafe fn create_device_from_hal<A: wgc::hub::HalApi>(
         &self,
         adapter: &wgc::id::AdapterId,
@@ -94,7 +94,7 @@ impl Context {
         Ok((device, device_id))
     }
 
-    #[cfg(not(target_arch = "wasm32"))]
+    #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
     pub unsafe fn create_texture_from_hal<A: wgc::hub::HalApi>(
         &self,
         hal_texture: A::Texture,
@@ -123,7 +123,7 @@ impl Context {
         }
     }
 
-    #[cfg(not(target_arch = "wasm32"))]
+    #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
     pub unsafe fn device_as_hal<A: wgc::hub::HalApi, F: FnOnce(Option<&A::Device>) -> R, R>(
         &self,
         device: &Device,
@@ -133,7 +133,7 @@ impl Context {
             .device_as_hal::<A, F, R>(device.id, hal_device_callback)
     }
 
-    #[cfg(not(target_arch = "wasm32"))]
+    #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
     pub unsafe fn texture_as_hal<A: wgc::hub::HalApi, F: FnOnce(Option<&A::Texture>)>(
         &self,
         texture: &Texture,
@@ -143,7 +143,7 @@ impl Context {
             .texture_as_hal::<A, F>(texture.id, hal_texture_callback)
     }
 
-    #[cfg(not(target_arch = "wasm32"))]
+    #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
     pub fn generate_report(&self) -> wgc::hub::GlobalReport {
         self.0.generate_report()
     }
@@ -1528,7 +1528,7 @@ impl crate::Context for Context {
 
     #[cfg_attr(target_arch = "wasm32", allow(unused))]
     fn device_drop(&self, device: &Self::DeviceId) {
-        #[cfg(not(target_arch = "wasm32"))]
+        #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
         {
             let global = &self.0;
             match wgc::gfx_select!(device.id => global.device_poll(device.id, true)) {
@@ -1537,7 +1537,7 @@ impl crate::Context for Context {
             }
         }
         //TODO: make this work in general
-        #[cfg(not(target_arch = "wasm32"))]
+        #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
         #[cfg(feature = "metal-auto-capture")]
         {
             let global = &self.0;
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index 76de67b980..414a68c6c5 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -1431,7 +1431,7 @@ impl Instance {
     /// # Safety
     ///
     /// Refer to the creation of wgpu-hal Instance for every backend.
-    #[cfg(not(target_arch = "wasm32"))]
+    #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
     pub unsafe fn from_hal<A: wgc::hub::HalApi>(hal_instance: A::Instance) -> Self {
         Self {
             context: Arc::new(C::from_hal_instance::<A>(hal_instance)),
@@ -1443,7 +1443,7 @@ impl Instance {
     /// # Arguments
     ///
     /// - `backends` - Backends from which to enumerate adapters.
-    #[cfg(not(target_arch = "wasm32"))]
+    #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
     pub fn enumerate_adapters(&self, backends: Backends) -> impl Iterator<Item = Adapter> {
         let context = Arc::clone(&self.context);
         self.context
@@ -1474,7 +1474,7 @@ impl Instance {
     /// # Safety
     ///
     /// `hal_adapter` must be created from this instance internal handle.
-    #[cfg(not(target_arch = "wasm32"))]
+    #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
     pub unsafe fn create_adapter_from_hal<A: wgc::hub::HalApi>(
         &self,
         hal_adapter: hal::ExposedAdapter<A>,
@@ -1555,7 +1555,7 @@ impl Instance {
     }
 
     /// Generates memory report.
-    #[cfg(not(target_arch = "wasm32"))]
+    #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
     pub fn generate_report(&self) -> wgc::hub::GlobalReport {
         self.context.generate_report()
     }
@@ -1607,7 +1607,7 @@ impl Adapter {
     ///
     /// - `hal_device` must be created from this adapter internal handle.
     /// - `desc.features` must be a subset of `hal_device` features.
-    #[cfg(not(target_arch = "wasm32"))]
+    #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
     pub unsafe fn create_device_from_hal<A: wgc::hub::HalApi>(
         &self,
         hal_device: hal::OpenDevice<A>,
@@ -1846,7 +1846,7 @@ impl Device {
     /// - `hal_texture` must be created from this device internal handle
     /// - `hal_texture` must be created respecting `desc`
     /// - `hal_texture` must be initialized
-    #[cfg(not(target_arch = "wasm32"))]
+    #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
     pub unsafe fn create_texture_from_hal<A: wgc::hub::HalApi>(
         &self,
         hal_texture: A::Texture,
@@ -1910,7 +1910,7 @@ impl Device {
     /// # Safety
     ///
     /// - The raw handle obtained from the hal Device must not be manually destroyed
-    #[cfg(not(target_arch = "wasm32"))]
+    #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
     pub unsafe fn as_hal<A: wgc::hub::HalApi, F: FnOnce(Option<&A::Device>) -> R, R>(
         &self,
         hal_device_callback: F,
@@ -2203,7 +2203,7 @@ impl Texture {
     /// # Safety
     ///
     /// - The raw handle obtained from the hal Texture must not be manually destroyed
-    #[cfg(not(target_arch = "wasm32"))]
+    #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
     pub unsafe fn as_hal<A: wgc::hub::HalApi, F: FnOnce(Option<&A::Texture>)>(
         &self,
         hal_texture_callback: F,