diff --git a/demo/main.js b/demo/main.js
index 0ec1f5e..f58732f 100644
--- a/demo/main.js
+++ b/demo/main.js
@@ -4,50 +4,46 @@ const RELAYS = ['wss://nostr.bitcoiner.social', 'wss://nostr.mom', 'wss://nos.lo
 
 const Relay = window.NostrTools.Relay;
 const SimplePool = window.NostrTools.SimplePool;
-const pool = new SimplePool();
-
 
 const secret = window.NostrTools.generateSecretKey();
 const pubkey = window.NostrTools.getPublicKey(secret);
 
+const pool = new SimplePool();
+let pubs = [];
+
+let NUM_WORKERS = navigator.hardwareConcurrency-1 || 2; // Or set a fixed number
+let workers = [];
+let isWorkerReady = 0;
+let isMining = false;
+
 const mineButton = document.getElementById('mineButton');
 const eventInput = document.getElementById('eventInput');
 const difficultyInput = document.getElementById('difficulty');
 const resultOutput = document.getElementById('result');
-const numberOfWorkers = document.getElementById('numberOfWorkers');
 const hashrateOutput = document.getElementById('hashrate');
 const cancelButton = document.getElementById('cancelButton');
 const relayStatus = document.getElementById('relayStatus');
 const neventOutput = document.getElementById('neventOutput');
-
-let NUM_WORKERS = navigator.hardwareConcurrency || 4; // Or set a fixed number
-let workers = [];
-let isWorkerReady = 0;
-let isMining = false;
-
-let pubs = [];
+const numberOfWorkers = document.getElementById('numberOfWorkers');
 
 numberOfWorkers.value = NUM_WORKERS;
+numberOfWorkers.max = navigator.hardwareConcurrency || 3;
 
-const MOVING_AVERAGE_WINDOW = 2;
-let recentHashRates = [];
+let workerHashRates = {};
 
 for (let i = 0; i < NUM_WORKERS; i++) {
     const worker = new Worker('./worker.js', { type: 'module' });
     worker.onmessage = handleWorkerMessage;
-    worker.postMessage({ type: 'init' });
+    worker.postMessage({ type: 'init', id: i });
     workers.push(worker);
 }
 
 function handleWorkerMessage(e) {
-    const { type, data, error, averageHashRate, workerId } = e.data;
+    const { type, data, error, hashRate, workerId } = e.data;
 
     if (type === 'progress') {
-        recentHashRates.push(averageHashRate);
-        if (recentHashRates.length > MOVING_AVERAGE_WINDOW * NUM_WORKERS) {
-            recentHashRates.shift();
-        }
-        const totalHashRate = recentHashRates.reduce((a, b) => a + b, 0);
+        workerHashRates[workerId] = hashRate;
+        const totalHashRate = Object.values(workerHashRates).reduce((a, b) => a + b, 0);
         hashrateOutput.textContent = `${(totalHashRate / 1000).toFixed(2)} kH/s`;
     } else if (type === 'ready') {
         isWorkerReady++;
@@ -75,11 +71,13 @@ ${JSON.stringify(data, null, 2)}
         hashrateOutput.textContent = '0 H/s';
         mineButton.disabled = false;
         isMining = false;
+        workerHashRates = {}; // Reset hash rates after mining
     } else if (type === 'error') {
         resultOutput.textContent = `Error: ${error}`;
         hashrateOutput.textContent = '0 H/s';
         mineButton.disabled = false;
         isMining = false;
+        workerHashRates = {}; // Reset hash rates on error
     }
 }
 
@@ -97,12 +95,12 @@ mineButton.addEventListener('click', () => {
     const content = eventInput.value.trim();
     const nostrEvent = generateEvent(content);
     const difficulty = parseInt(difficultyInput.value, 10);
-    NUM_WORKERS = parseInt(numberOfWorkers.value, 10);
+    const NUM_WORKERS = parseInt(numberOfWorkers.value, 10);
 
     relayStatus.textContent = '';
     neventOutput.textContent = '';
     resultOutput.textContent = '';
-    recentHashRates = [];
+    workerHashRates = {};
 
     if (!content) {
         alert('Please enter content for the Nostr event.');
@@ -145,6 +143,7 @@ cancelButton.addEventListener('click', () => {
         hashrateOutput.textContent = '0 H/s';
         mineButton.disabled = false;
         isMining = false;
+        workerHashRates = {}; // Reset hash rates after cancellation
     }
 });
 
@@ -232,3 +231,239 @@ const showRelayStatus = () => {
         });
     });
 }
+
+
+// const CLIENT = 'notemine';
+// const TOPIC = 'notemine';
+// const RELAYS = ['wss://nostr.bitcoiner.social', 'wss://nostr.mom', 'wss://nos.lol', 'wss://powrelay.xyz', 'wss://labour.fiatjaf.com/', 'wss://nostr.lu.ke', 'wss://140.f7z.io'];
+
+// const Relay = window.NostrTools.Relay;
+// const SimplePool = window.NostrTools.SimplePool;
+// const pool = new SimplePool();
+
+
+// const secret = window.NostrTools.generateSecretKey();
+// const pubkey = window.NostrTools.getPublicKey(secret);
+
+// const mineButton = document.getElementById('mineButton');
+// const eventInput = document.getElementById('eventInput');
+// const difficultyInput = document.getElementById('difficulty');
+// const resultOutput = document.getElementById('result');
+// const hashrateOutput = document.getElementById('hashrate');
+// const cancelButton = document.getElementById('cancelButton');
+// const relayStatus = document.getElementById('relayStatus');
+// const neventOutput = document.getElementById('neventOutput');
+// const numberOfWorkers = document.getElementById('numberOfWorkers');
+
+// let NUM_WORKERS = navigator.hardwareConcurrency || 4; // Or set a fixed number
+// let workers = [];
+// let isWorkerReady = 0;
+// let isMining = false;
+
+// let pubs = [];
+
+// numberOfWorkers.value = NUM_WORKERS;
+
+// const MOVING_AVERAGE_WINDOW = 2;
+// let recentHashRates = [];
+
+// for (let i = 0; i < NUM_WORKERS; i++) {
+//     const worker = new Worker('./worker.js', { type: 'module' });
+//     worker.onmessage = handleWorkerMessage;
+//     worker.postMessage({ type: 'init' });
+//     workers.push(worker);
+// }
+
+// function handleWorkerMessage(e) {
+//     const { type, data, error, averageHashRate, workerId } = e.data;
+
+//     if (type === 'progress') {
+//         recentHashRates.push(averageHashRate);
+//         if (recentHashRates.length > MOVING_AVERAGE_WINDOW * NUM_WORKERS) {
+//             recentHashRates.shift();
+//         }
+//         const totalHashRate = recentHashRates.reduce((a, b) => a + b, 0);
+//         hashrateOutput.textContent = `${(totalHashRate / 1000).toFixed(2)} kH/s`;
+//     } else if (type === 'ready') {
+//         isWorkerReady++;
+//         if (isWorkerReady === NUM_WORKERS) {
+//             console.log('All workers are ready.');
+//             mineButton.disabled = false;
+//             resultOutput.textContent = 'Workers are ready. You can start mining.';
+//         }
+//     } else if (type === 'result') {
+//         if (data.error) {
+//             resultOutput.textContent = `
+// Error: ${data.error}
+// ${JSON.stringify(data, null, 2)}
+//             `;
+//         } else {
+//             try {
+//                 resultOutput.textContent = JSON.stringify(data, null, 2);
+//                 publishEvent(data.event);
+//                 cancelOtherWorkers(workerId);
+//             } catch (e) {
+//                 console.error('Error publishing event:', e);
+//                 resultOutput.textContent = `Error publishing event: ${e.message}`;
+//             }
+//         }
+//         hashrateOutput.textContent = '0 H/s';
+//         mineButton.disabled = false;
+//         isMining = false;
+//     } else if (type === 'error') {
+//         resultOutput.textContent = `Error: ${error}`;
+//         hashrateOutput.textContent = '0 H/s';
+//         mineButton.disabled = false;
+//         isMining = false;
+//     }
+// }
+
+// function cancelOtherWorkers(excludeWorkerId) {
+//     workers.forEach((worker, index) => {
+//         if (index !== excludeWorkerId) {
+//             worker.postMessage({ type: 'cancel' });
+//         }
+//     });
+// }
+
+// mineButton.addEventListener('click', () => {
+//     if (isMining) return;
+
+//     const content = eventInput.value.trim();
+//     const nostrEvent = generateEvent(content);
+//     const difficulty = parseInt(difficultyInput.value, 10);
+//     NUM_WORKERS = parseInt(numberOfWorkers.value, 10);
+
+//     relayStatus.textContent = '';
+//     neventOutput.textContent = '';
+//     resultOutput.textContent = '';
+//     recentHashRates = [];
+
+//     if (!content) {
+//         alert('Please enter content for the Nostr event.');
+//         return;
+//     }
+
+//     if (isNaN(difficulty) || difficulty <= 0) {
+//         alert('Please enter a valid difficulty.');
+//         return;
+//     }
+
+//     if (isWorkerReady < NUM_WORKERS) {
+//         alert('Workers are not ready yet. Please wait.');
+//         return;
+//     }
+
+//     const event = JSON.stringify(nostrEvent);
+
+//     mineButton.disabled = true;
+//     resultOutput.textContent = 'Mining in progress...';
+//     hashrateOutput.textContent = '0 H/s';
+//     isMining = true;
+
+//     workers.forEach((worker, index) => {
+//         worker.postMessage({
+//             type: 'mine',
+//             event,
+//             difficulty: difficulty,
+//             workerId: index,
+//         });
+//     });
+// });
+
+// cancelButton.addEventListener('click', () => {
+//     if (isMining) {
+//         workers.forEach(worker => {
+//             worker.postMessage({ type: 'cancel' });
+//         });
+//         resultOutput.textContent = 'Mining cancellation requested.';
+//         hashrateOutput.textContent = '0 H/s';
+//         mineButton.disabled = false;
+//         isMining = false;
+//     }
+// });
+
+// const getPow = (hex) => {
+//     let count = 0;
+
+//     for (let i = 0; i < hex.length; i++) {
+//         const nibble = parseInt(hex[i], 16);
+//         if (nibble === 0) {
+//             count += 4;
+//         } else {
+//             count += Math.clz32(nibble) - 28;
+//             break;
+//         }
+//     }
+
+//     return count;
+// }
+
+// const verifyPow = (event) => {
+//     const hash = window.NostrTools.getEventHash(event);
+//     const count = getPow(hash);
+//     const nonceTag = event.tags.find(tag => tag[0] === 'nonce');
+//     if (!nonceTag || nonceTag.length < 3) {
+//         return 0;
+//     }
+//     const targetDifficulty = parseInt(nonceTag[2], 10);
+//     return Math.min(count, targetDifficulty);
+// }
+
+// const generateEvent = (content) => {
+//     return {
+//         pubkey,
+//         kind: 1,
+//         tags: [['t', TOPIC], ['client', CLIENT]],
+//         content,
+//     }
+// }
+
+// const generateNEvent = (event) => {
+//     const { id, pubkey: author } = event;
+//     const pointer = { id, pubkey, relays: RELAYS };
+//     return window.NostrTools.nip19.neventEncode(pointer);
+// }
+
+// const publishEvent = async (ev) => {
+//     const pow = verifyPow(ev);
+
+//     if (!pow || getPow(ev.id) < pow) {
+//         resultOutput.textContent = `Error: Invalid POW ${pow}`;
+//         return;
+//     }
+//     console.log('Publishing event:', ev);
+//     try {
+//         ev = window.NostrTools.finalizeEvent(ev, secret);
+//         let isGood = window.NostrTools.verifyEvent(ev);
+//         if (!isGood) throw new Error('Event is not valid');
+//         pubs = pool.publish(RELAYS, ev);
+//         await Promise.allSettled(pubs);
+//         showRelayStatus();
+//         console.log('Event published successfully.');
+//         neventOutput.textContent = generateNEvent(ev);
+//     } catch (error) {
+//         console.error('Error publishing event:', error);
+//         resultOutput.textContent = `Error publishing event: ${error.message}`;
+//     }
+// };
+
+// let settledCount = 0;
+
+// const showRelayStatus = () => {
+//     const settled = Array(pubs.length).fill(false);
+//     const intervalId = setInterval(() => {
+//         settledCount = settled.filter(Boolean).length;
+//         relayStatus.textContent = `Published to ${settledCount}/${pubs.length} relays.`;
+//         if (settledCount === pubs.length) {
+//             clearInterval(intervalId);
+//         }
+//     }, 100);
+
+//     pubs.forEach((pub, index) => {
+//         pub.finally(() => {
+//             relayStatus.textContent = `Published to all relays [${RELAYS.join(', ')}]`;
+//             settled[index] = true;
+//         });
+//     });
+// }
diff --git a/demo/worker.js b/demo/worker.js
index 2d61cd6..27a1444 100644
--- a/demo/worker.js
+++ b/demo/worker.js
@@ -2,28 +2,26 @@ import init, { mine_event } from './pkg/notemine.js';
 
 let wasm;
 let mining = false;
+let workerId;
 
 async function initWasm() {
     try {
         wasm = await init({});
-        postMessage({ type: 'ready' });
+        postMessage({ type: 'ready', workerId });
     } catch (error) {
-        postMessage({ type: 'error', error: `WASM initialization failed: ${error.message}` });
+        postMessage({ type: 'error', error: `WASM initialization failed: ${error.message}`, workerId });
     }
 }
 
-initWasm();
-
-function reportProgress(averageHashRate) {
-    postMessage({ type: 'progress', averageHashRate, workerId });
+function reportProgress(hashRate) {
+    postMessage({ type: 'progress', hashRate, workerId });
 }
 
-let workerId;
-
 self.onmessage = async function (e) {
     const { type, event, difficulty, id } = e.data;
     if (type === 'init') {
         workerId = id;
+        initWasm();
     } else if (type === 'mine' && !mining) {
         mining = true;
         try {
@@ -39,5 +37,7 @@ self.onmessage = async function (e) {
         }
     } else if (type === 'cancel' && mining) {
         console.log('Mining cancellation requested.');
+        // Implement cancellation logic if possible
+        mining = false;
     }
 };
diff --git a/src/lib.rs b/src/lib.rs
index ab2c4bb..59a5214 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,5 @@
 use serde::{Deserialize, Serialize};
-use serde_wasm_bindgen::to_value;
+use serde_json::to_string;
 
 use sha2::{Digest, Sha256};
 use wasm_bindgen::prelude::*;
@@ -24,6 +24,13 @@ pub struct MinedResult {
     pub khs: f64,
 }
 
+fn serialize_u64_as_number<S>(x: &u64, s: S) -> Result<S::Ok, S::Error>
+where
+    S: serde::Serializer,
+{
+    s.serialize_u64(*x)
+}
+
 #[derive(Serialize)]
 struct HashableEvent<'a>(
     u32,
@@ -35,13 +42,6 @@ struct HashableEvent<'a>(
     &'a str,
 );
 
-fn serialize_u64_as_number<S>(x: &u64, s: S) -> Result<S::Ok, S::Error>
-where
-    S: serde::Serializer,
-{
-    s.serialize_u64(*x)
-}
-
 #[inline]
 fn get_event_hash(event: &NostrEvent) -> Vec<u8> {
     let hashable_event = HashableEvent(
@@ -53,13 +53,11 @@ fn get_event_hash(event: &NostrEvent) -> Vec<u8> {
         &event.content,
     );
 
-    let serialized_str = match serde_json::to_string(&hashable_event) {
+    let serialized_str = match to_string(&hashable_event) {
         Ok(s) => s,
         Err(_) => return vec![],
     };
 
-    println!("Serialized event: {}", serialized_str);
-
     let hash_bytes = Sha256::digest(serialized_str.as_bytes()).to_vec();
     hash_bytes
 }
@@ -93,7 +91,7 @@ pub fn mine_event(
         Ok(e) => e,
         Err(err) => {
             console::log_1(&format!("JSON parsing error: {}", err).into());
-            return to_value(&serde_json::json!({
+            return serde_wasm_bindgen::to_value(&serde_json::json!({
                 "error": format!("Invalid event JSON: {}", err)
             }))
             .unwrap_or(JsValue::NULL);
@@ -121,16 +119,13 @@ pub fn mine_event(
         Ok(func) => func,
         Err(_) => {
             console::log_1(&"Failed to convert report_progress to Function".into());
-            return to_value(&serde_json::json!({
+            return serde_wasm_bindgen::to_value(&serde_json::json!({
                 "error": "Invalid progress callback."
             }))
             .unwrap_or(JsValue::NULL);
         }
     };
 
-    const MOVING_AVERAGE_WINDOW: usize = 5;
-    let mut recent_hash_rates: Vec<f64> = Vec::with_capacity(MOVING_AVERAGE_WINDOW);
-
     let start_time = js_sys::Date::now();
     let mut nonce: u64 = 0;
     let mut total_hashes: u64 = 0;
@@ -151,7 +146,7 @@ pub fn mine_event(
         let hash_bytes = get_event_hash(&event);
         if hash_bytes.is_empty() {
             console::log_1(&"Failed to compute event hash.".into());
-            return to_value(&serde_json::json!({
+            return serde_wasm_bindgen::to_value(&serde_json::json!({
                 "error": "Failed to compute event hash."
             }))
             .unwrap_or(JsValue::NULL);
@@ -175,19 +170,18 @@ pub fn mine_event(
             };
 
             console::log_1(&format!("Mined successfully with nonce: {}", nonce).into());
-            return to_value(&result).unwrap_or(JsValue::NULL);
+            return serde_wasm_bindgen::to_value(&result).unwrap_or(JsValue::NULL);
         }
 
         nonce += 1;
 
         if nonce % report_interval == 0 {
             let current_time = js_sys::Date::now();
-            let elapsed_time = (current_time - last_report_time) / 1000.0; // seconds
+            let elapsed_time = (current_time - last_report_time) / 1000.0;
             if elapsed_time > 0.0 {
-                let hash_rate = report_interval as f64; // Number of hashes
-                // Send both hash count and elapsed time
+                let hash_rate = (report_interval as f64) / elapsed_time;
                 report_progress
-                    .call2(&JsValue::NULL, &hash_rate.into(), &elapsed_time.into())
+                    .call1(&JsValue::NULL, &hash_rate.into())
                     .unwrap_or_else(|err| {
                         console::log_1(
                             &format!("Error calling progress callback: {:?}", err).into(),
@@ -201,10 +195,19 @@ pub fn mine_event(
         if nonce % 100_000 == 0 {
             console::log_1(&format!("Checked nonce up to: {}", nonce).into());
         }
+
+        if nonce >= 10_000_000 {
+            console::log_1(&"Reached maximum nonce limit without finding a valid hash".into());
+            return serde_wasm_bindgen::to_value(&serde_json::json!({
+                "error": "Reached maximum nonce limit without finding a valid hash"
+            }))
+            .unwrap_or(JsValue::NULL);
+        }
     }
 }
 
 
+
 #[cfg(test)]
 mod tests {
     use super::*;