diff --git a/frontends/web/ts/gb.ts b/frontends/web/ts/gb.ts
index c37cc617c8995b732006f997635da148de27b950..d95d500678de2f3235e5b4f3c7b17544eb575641 100644
--- a/frontends/web/ts/gb.ts
+++ b/frontends/web/ts/gb.ts
@@ -804,22 +804,22 @@ export class GameboyEmulator extends EmulatorBase implements Emulator {
 
     serializeState(): Uint8Array {
         if (!this.gameBoy) throw new Error("Unable to serialize state");
-        return StateManager.save(this.gameBoy, SaveStateFormat.Bos);
+        return StateManager.save_ws(this.gameBoy, SaveStateFormat.Bos);
     }
 
     unserializeState(data: Uint8Array) {
         if (!this.gameBoy) throw new Error("Unable to unserialize state");
-        StateManager.load(data, this.gameBoy, SaveStateFormat.Bos);
+        StateManager.load_ws(data, this.gameBoy, SaveStateFormat.Bos);
     }
 
     buildState(index: number, data: Uint8Array): SaveState {
-        const state = StateManager.read_bos(data);
+        const state = StateManager.read_bos_ws(data);
         return {
             index: index,
-            timestamp: Number(state.timestamp()),
-            agent: state.agent(),
-            model: state.model(),
-            thumbnail: state.image_eager()
+            timestamp: Number(state.timestamp_ws()),
+            agent: state.agent_ws(),
+            model: state.model_ws(),
+            thumbnail: state.image_eager_ws()
         };
     }
 
diff --git a/src/error.rs b/src/error.rs
index 9d364a0206f7bce615067aae0c905ab5ce1e1409..d33d4d26edbb69f54c818f8b794335177cccc992 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,11 +1,5 @@
 use std::fmt::{self, Display, Formatter};
 
-#[cfg(feature = "wasm")]
-use wasm_bindgen::prelude::*;
-
-#[cfg(feature = "wasm")]
-use wasm_bindgen::convert::*;
-
 /// Top level enum for error handling.
 /// Most of the time, you will want to use the `CustomError` variant
 /// to provide a more detailed error message.
@@ -33,13 +27,3 @@ impl Display for Error {
         write!(f, "{}", self.description())
     }
 }
-
-#[cfg(feature = "wasm")]
-impl IntoWasmAbi for Error {
-    type Abi = <Vec<u8> as IntoWasmAbi>::Abi;
-
-    #[inline]
-    fn into_abi(self) -> Self::Abi {
-        self.description().into_abi();
-    }
-}
diff --git a/src/state.rs b/src/state.rs
index 0d6f0775764fe57dfe1e351b562d1a9cbfb91553..166289b28abf2fe60c87790417e66244d3283aa8 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -128,7 +128,6 @@ impl BosState {
     }
 }
 
-#[cfg_attr(feature = "wasm", wasm_bindgen)]
 impl BosState {
     pub fn timestamp(&self) -> Result<u64, Error> {
         if let Some(info) = &self.info {
@@ -163,6 +162,26 @@ impl BosState {
     }
 }
 
+#[cfg(feature = "wasm")]
+#[cfg_attr(feature = "wasm", wasm_bindgen)]
+impl BosState {
+    pub fn timestamp_ws(&self) -> Result<u64, String> {
+        Self::timestamp(self).map_err(|e| e.to_string())
+    }
+
+    pub fn agent_ws(&self) -> Result<String, String> {
+        Self::agent(self).map_err(|e| e.to_string())
+    }
+
+    pub fn model_ws(&self) -> Result<String, String> {
+        Self::model(self).map_err(|e| e.to_string())
+    }
+
+    pub fn image_eager_ws(&self) -> Result<Vec<u8>, String> {
+        Self::image_eager(self).map_err(|e| e.to_string())
+    }
+}
+
 impl Serialize for BosState {
     fn write(&mut self, buffer: &mut Cursor<Vec<u8>>) {
         self.block_count = self.build_block_count();
@@ -1465,7 +1484,6 @@ impl StateManager {
     }
 }
 
-#[cfg_attr(feature = "wasm", wasm_bindgen)]
 impl StateManager {
     pub fn save(gb: &mut GameBoy, format: Option<SaveStateFormat>) -> Result<Vec<u8>, Error> {
         let mut data = Cursor::new(vec![]);
@@ -1563,6 +1581,34 @@ impl StateManager {
     }
 }
 
+#[cfg(feature = "wasm")]
+#[cfg_attr(feature = "wasm", wasm_bindgen)]
+impl StateManager {
+    pub fn save_ws(gb: &mut GameBoy, format: Option<SaveStateFormat>) -> Result<Vec<u8>, String> {
+        Self::save(gb, format).map_err(|e| e.to_string())
+    }
+
+    pub fn load_ws(
+        data: &[u8],
+        gb: &mut GameBoy,
+        format: Option<SaveStateFormat>,
+    ) -> Result<(), String> {
+        Self::load(data, gb, format).map_err(|e| e.to_string())
+    }
+
+    pub fn read_bos_ws(data: &[u8]) -> Result<BosState, String> {
+        Self::read_bos(data).map_err(|e| e.to_string())
+    }
+
+    pub fn read_bess_ws(data: &[u8]) -> Result<BessState, String> {
+        Self::read_bess(data).map_err(|e| e.to_string())
+    }
+
+    pub fn thumbnail_ws(data: &[u8], format: Option<SaveStateFormat>) -> Result<Vec<u8>, String> {
+        Self::thumbnail(data, format).map_err(|e| e.to_string())
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;