diff --git a/404.html b/404.html index 58cb45b..5816f76 100644 --- a/404.html +++ b/404.html @@ -377,7 +377,7 @@

Legal

- + diff --git a/about/index.html b/about/index.html index 1d1303c..82d13f4 100644 --- a/about/index.html +++ b/about/index.html @@ -1028,7 +1028,7 @@

Legal

- + diff --git a/charging-port-fix/index.html b/charging-port-fix/index.html index b6e195f..532ea43 100644 --- a/charging-port-fix/index.html +++ b/charging-port-fix/index.html @@ -763,6 +763,6 @@

Legal

- + diff --git a/contact/index.html b/contact/index.html index e5bc9d4..d25c1bb 100644 --- a/contact/index.html +++ b/contact/index.html @@ -804,7 +804,7 @@

Legal

- + + diff --git a/disclaimer/index.html b/disclaimer/index.html index 360a7f5..383d0b1 100644 --- a/disclaimer/index.html +++ b/disclaimer/index.html @@ -805,7 +805,7 @@

Legal

- + diff --git a/frequency-generator/index.html b/frequency-generator/index.html index ab19d8c..19064bd 100644 --- a/frequency-generator/index.html +++ b/frequency-generator/index.html @@ -740,6 +740,6 @@

Legal

- + diff --git a/game/index.html b/game/index.html index 4154ea4..4703b4b 100644 --- a/game/index.html +++ b/game/index.html @@ -915,7 +915,7 @@

Legal

- + diff --git a/how-water-eject-works.jpg b/how-water-eject-works.jpg new file mode 100644 index 0000000..f1d50d3 Binary files /dev/null and b/how-water-eject-works.jpg differ diff --git a/index.html b/index.html index 5fe1fd3..482f0b8 100644 --- a/index.html +++ b/index.html @@ -28,21 +28,23 @@ - - - - + + + + + - + - + + @@ -133,7 +135,8 @@ "price": "0", "priceCurrency": "USD" }, - "description": "A free web-based tool that plays specific frequency sounds to eject water and dust from smartphone and laptop speakers." + "description": "A free web-based tool that plays specific frequency sounds to eject water and dust from smartphone and laptop speakers.", + "image": "https://waterejector.devdeskapp.com/og-image.jpg" }, { "@type": "VideoObject", @@ -146,6 +149,92 @@ "uploadDate": "2026-10-08T00:30:00+05:30", "duration": "PT30S", "contentUrl": "https://waterejector.devdeskapp.com/how-it-works.mp4" + }, + { + "@type": "FAQPage", + "@id": "https://waterejector.devdeskapp.com/#faq", + "mainEntity": [ + { + "@type": "Question", + "name": "How does the water eject sound tool work?", + "acceptedAnswer": { + "@type": "Answer", + "text": "Our speaker cleaner uses precisely calibrated audio frequencies to create sound waves that vibrate your speaker membrane. These vibrations generate mechanical force that pushes water droplets and dust particles out through the speaker grilles. The water eject mode uses 165Hz frequency, while dust removal cycles through multiple frequencies for comprehensive cleaning." + } + }, + { + "@type": "Question", + "name": "Is this safe for my device?", + "acceptedAnswer": { + "@type": "Answer", + "text": "Yes, for normal use. The tool plays ordinary sine tones (165Hz for water, 200-1500Hz for dust) within the range phone speakers are built to reproduce - nothing is inserted into the device. Unlike pins, cotton swabs or compressed air, it never touches the speaker mesh. Avoid running it for long periods at maximum volume on a speaker that is already crackling or damaged." + } + }, + { + "@type": "Question", + "name": "Which devices are compatible with this tool?", + "acceptedAnswer": { + "@type": "Answer", + "text": "Our sound cleaner works with any device that has speakers and can access a web browser - including iPhones, Samsung phones, Google Pixel, Android devices, tablets, iPads, laptops, and desktop computers. No app download or installation required." + } + }, + { + "@type": "Question", + "name": "How long does the cleaning process take?", + "acceptedAnswer": { + "@type": "Answer", + "text": "Each cleaning cycle takes approximately 60-90 seconds. For best results when trying to fix a muffled speaker, we recommend running the water eject mode once, followed by the vibration mode, then testing your audio. If needed, repeat the process 2-3 times for stubborn moisture or debris." + } + }, + { + "@type": "Question", + "name": "Do I need to download an app?", + "acceptedAnswer": { + "@type": "Answer", + "text": "No! WaterEjector is 100% web-based and works directly in your browser. Simply visit our website, and you can start cleaning immediately. This makes it more convenient than apps and accessible from any device without storage space concerns." + } + }, + { + "@type": "Question", + "name": "What should I do if sound is still muffled after cleaning?", + "acceptedAnswer": { + "@type": "Answer", + "text": "First, try repeating the process 2-3 times, ensuring your volume is at maximum. Use both water eject and vibration modes. If the issue persists, there may be internal speaker damage or a blown driver that requires repair. Our acoustic cleaner tool resolves issues caused by trapped moisture or dust, but permanent physical damage needs expert repair." + } + }, + { + "@type": "Question", + "name": "Can this fix speakers damaged by salt water?", + "acceptedAnswer": { + "@type": "Answer", + "text": "Our speaker dust cleaning sound can help eject salt water, but salt water damage requires immediate action. Use our tool immediately after exposure, then rinse the device exterior with fresh water (if waterproof) and repeat the cleaning process several times. Salt residue can cause corrosion, so act fast." + } + }, + { + "@type": "Question", + "name": "Is this really free? Are there hidden costs?", + "acceptedAnswer": { + "@type": "Answer", + "text": "Yes, completely free! No subscriptions, no in-app purchases, no premium features locked behind paywalls. We believe everyone should have access to this essential speaker cleaning tool without paying for expensive repairs or paid apps." + } + }, + { + "@type": "Question", + "name": "How often should I use the speaker cleaner?", + "acceptedAnswer": { + "@type": "Answer", + "text": "Use it whenever you notice reduced sound quality, after water exposure, or as preventive maintenance. For daily phone users, running the sound cleaner weekly helps maintain optimal audio performance. There's no harm in regular use - it only takes a minute and keeps speakers in peak condition." + } + }, + { + "@type": "Question", + "name": "What's the difference between left, right, and both speaker modes?", + "acceptedAnswer": { + "@type": "Answer", + "text": "Most devices have stereo speakers. If only one speaker has water or dust issues, you can target that specific speaker to save time. \"Both speakers\" mode is recommended for general cleaning, while individual speaker selection is useful when only one side needs attention after a targeted spill or blockage." + } + } + ] } ] } @@ -585,6 +674,14 @@

How to Get Water Out of Phone Speaker?

+
+ How the 165Hz water eject sound works: 1) set volume to max and place the phone speaker-down, 2) 1-second 165Hz pulses with 0.3-second gaps vibrate the speaker cone, 3) water droplets are pushed out through the speaker grille +
+ How the 165Hz water eject sound pushes water out of a phone speaker in 3 steps.
+
+
@@ -647,7 +744,7 @@

Vibration Enhancement

-

Step-by-Step Guide to WaterEjector

+

Step-by-Step: How to Eject Water from Your Phone Speaker

@@ -765,10 +862,10 @@

Universal Compatibility

-

100% Safe

+

Safe & Non-Invasive

-

Our sound cleaner uses audio frequencies within safe ranges that won't damage your speakers or - device components.

+

Uses ordinary audio tones your speaker is built to play - no pins, swabs or compressed air + touching the delicate speaker mesh.

@@ -781,7 +878,7 @@

100% Safe

Lightning Fast

-

Complete speaker cleaning in under 2 minutes. WaterEjector issues instantly +

Complete speaker cleaning in under 2 minutes. Fix muffled sound instantly without trips to repair shops.

@@ -1048,10 +1145,10 @@

Frequently Asked Questions

-

Absolutely! The speaker cleaner sound uses frequencies within normal audio ranges that your - speakers are designed to handle. Unlike physical cleaning methods that risk damaging - delicate components, sound wave technology is completely non-invasive and safe for all - device types.

+

Yes, for normal use. The tool plays ordinary sine tones (165Hz for water, 200-1500Hz for dust) + within the range phone speakers are built to reproduce - nothing is inserted into the device. + Unlike pins, cotton swabs or compressed air, it never touches the speaker mesh. Avoid running + it for long periods at maximum volume on a speaker that is already crackling or damaged.

@@ -1079,8 +1176,8 @@

Frequently Asked Questions

-

Each cleaning cycle takes approximately 60-90 seconds. For best results when trying to fix my - speaker, we recommend running the water eject mode once, followed by the vibration mode, +

Each cleaning cycle takes approximately 60-90 seconds. For best results when trying to fix a + muffled speaker, we recommend running the water eject mode once, followed by the vibration mode, then testing your audio. If needed, repeat the process 2-3 times for stubborn moisture or debris.

@@ -1482,8 +1579,8 @@

Sonic Droplet Challenge

-

Ready to WaterEjector Problems for Good?

-

Join hundreds of thousands who have restored their device audio with our free speaker cleaner tool +

Ready to Fix Your Muffled Speaker?

+

Run the free 165Hz water eject sound now - no app, no sign-up, works on iPhone and Android.

Start Cleaning Now
@@ -1553,7 +1650,7 @@

Legal

- + diff --git a/mic-test/index.html b/mic-test/index.html index 654395f..d617741 100644 --- a/mic-test/index.html +++ b/mic-test/index.html @@ -737,6 +737,6 @@

Legal

- + diff --git a/og-image.jpg b/og-image.jpg new file mode 100644 index 0000000..0a62d61 Binary files /dev/null and b/og-image.jpg differ diff --git a/privacy-policy/index.html b/privacy-policy/index.html index dc5cc11..487e2ed 100644 --- a/privacy-policy/index.html +++ b/privacy-policy/index.html @@ -725,7 +725,7 @@

Legal

- + diff --git a/script.js b/script.js index 2204f0d..f52e838 100644 --- a/script.js +++ b/script.js @@ -23,6 +23,9 @@ let currentMode = "water"; let currentSpeaker = "both"; let progressInterval; let vibrationInterval; +// Incremented on every start/stop so a stopped run's pending timers can't +// finish (or cut short) a newer run. +let runId = 0; const NEXT_STEPS = { water: { title: "Water ejection complete!", @@ -129,6 +132,9 @@ function initializeAudioContext() { function setupEventListeners() { const startBtn = document.getElementById("startBtn"); const stopBtn = document.getElementById("stopBtn"); + // Game pages reuse id="startBtn" for their own Play button; only the cleaner + // tool (which has a Stop button) should get the cleaning handlers. + if (!stopBtn) return; const modeBtns = document.querySelectorAll("button.mode-btn"); const speakerBtns = document.querySelectorAll(".speaker-btn"); @@ -171,36 +177,52 @@ async function startCleaning() { await audioContext.resume(); } isPlaying = true; + const myRun = ++runId; updateUIState(true); hideNextStep(); updateProgress(0, "Starting..."); switch (currentMode) { case "water": - await waterEjectMode(); + await waterEjectMode(myRun); break; case "dust": - await dustRemovalMode(); + await dustRemovalMode(myRun); break; case "vibrate": - await vibrationMode(); + await vibrationMode(myRun); break; } } +function isCurrentRun(id) { + return isPlaying && id === runId; +} function stopCleaning() { isPlaying = false; + runId++; stopAllAudio(); stopVibration(); clearInterval(progressInterval); updateProgress(0, "Stopped"); updateUIState(false); + resetToneButtons(); +} +// Stereo / frequency pages: put their toggle buttons back in the idle state +// (also runs when playback is stopped because the tab was hidden). +function resetToneButtons() { + const stereoBtn = document.getElementById("startStereoBtn"); + const stereoProgress = document.getElementById("stereoProgress"); + const freqBtn = document.getElementById("startFreqBtn"); + if (stereoBtn) stereoBtn.textContent = "Play Test Sound"; + if (stereoProgress) stereoProgress.style.display = "none"; + if (freqBtn) freqBtn.textContent = "Play Frequency"; } -async function waterEjectMode() { +async function waterEjectMode(myRun) { updateProgress(0, "Ejecting water..."); const duration = 60000; playPulsedFrequency(165, duration, 1000, 300); animateProgress(duration, "Ejecting water..."); await sleep(duration); - if (isPlaying) { + if (isCurrentRun(myRun)) { updateProgress(100, "Water ejection complete!"); stopAllAudio(); isPlaying = false; @@ -208,34 +230,35 @@ async function waterEjectMode() { showNextStep("water"); } } -async function dustRemovalMode() { +async function dustRemovalMode(myRun) { updateProgress(0, "Removing dust..."); const frequencies = [200, 300, 450, 700, 1000, 1500]; const durationPerFreq = 10000; const totalDuration = frequencies.length * durationPerFreq; let elapsed = 0; - for (let i = 0; i < frequencies.length && isPlaying; i++) { + for (let i = 0; i < frequencies.length && isCurrentRun(myRun); i++) { const freq = frequencies[i]; playFrequency(freq, durationPerFreq); const startTime = Date.now(); const endTime = startTime + durationPerFreq; - while (Date.now() < endTime && isPlaying) { + while (Date.now() < endTime && isCurrentRun(myRun)) { elapsed = i * durationPerFreq + (Date.now() - startTime); const progress = (elapsed / totalDuration) * 100; updateProgress(progress, `Removing dust... ${freq}Hz`); await sleep(100); } + if (!isCurrentRun(myRun)) return; stopAllAudio(); await sleep(500); } - if (isPlaying) { + if (isCurrentRun(myRun)) { updateProgress(100, "Dust removal complete!"); isPlaying = false; updateUIState(false); showNextStep("dust"); } } -async function vibrationMode() { +async function vibrationMode(myRun) { const canVibrate = "vibrate" in navigator; const statusText = canVibrate ? "Vibrating..." : "Deep bass mode..."; updateProgress(0, "Vibration mode active..."); @@ -246,7 +269,7 @@ async function vibrationMode() { } animateProgress(duration, statusText); await sleep(duration); - if (isPlaying) { + if (isCurrentRun(myRun)) { updateProgress(100, "Vibration complete!"); stopAllAudio(); stopVibration(); @@ -359,6 +382,8 @@ function updateProgress(percent, status) { const progressFill = document.getElementById("progressFill"); const progressPercent = document.getElementById("progressPercent"); const statusText = document.getElementById("statusText"); + // Stereo / frequency / game pages have no progress bar. + if (!progressFill || !progressPercent || !statusText) return; progressFill.style.width = `${percent}%`; progressPercent.textContent = `${Math.round(percent)}%`; statusText.textContent = status; @@ -448,6 +473,7 @@ function updateUIState(playing) { const stopBtn = document.getElementById("stopBtn"); const modeBtns = document.querySelectorAll(".mode-btn"); const speakerBtns = document.querySelectorAll(".speaker-btn"); + if (!startBtn || !stopBtn) return; startBtn.disabled = playing; stopBtn.disabled = !playing; modeBtns.forEach((btn) => (btn.disabled = playing)); @@ -651,7 +677,10 @@ let stereoPanner = null; const startStereoBtn = document.getElementById("startStereoBtn"); if (startStereoBtn) { const speakerBtns = document.querySelectorAll(".speaker-btn"); - let currentPan = 0; + // Start on whichever channel button is marked active in the HTML (Left), + // so the sound matches what the UI shows. + const initialActive = document.querySelector(".speaker-btn.active[data-pan]"); + let currentPan = initialActive ? parseFloat(initialActive.dataset.pan) : 0; speakerBtns.forEach((btn) => { btn.addEventListener("click", () => { @@ -667,8 +696,6 @@ if (startStereoBtn) { startStereoBtn.addEventListener("click", async () => { if (isPlaying) { stopCleaning(); - startStereoBtn.textContent = "Play Test Sound"; - document.getElementById("stereoProgress").style.display = "none"; showNextStep("stereo", true); return; } @@ -717,7 +744,6 @@ if (freqSlider && startFreqBtn) { startFreqBtn.addEventListener("click", async () => { if (isPlaying) { stopCleaning(); - startFreqBtn.textContent = "Play Frequency"; showNextStep("frequency", true); return; } @@ -804,7 +830,8 @@ if (recordBtn) { }; mediaRecorder.onstop = () => { - const audioBlob = new Blob(audioChunks, { type: 'audio/webm' }); + // Safari/iPhone records audio/mp4, not webm - use the real type so playback works. + const audioBlob = new Blob(audioChunks, { type: mediaRecorder.mimeType || 'audio/webm' }); const audioUrl = URL.createObjectURL(audioBlob); audioPlayback.src = audioUrl; diff --git a/sonic-droplet/index.html b/sonic-droplet/index.html index 89e818b..26a45a2 100644 --- a/sonic-droplet/index.html +++ b/sonic-droplet/index.html @@ -990,7 +990,7 @@

Legal

- + diff --git a/stereo-test/index.html b/stereo-test/index.html index 117bde8..b10768b 100644 --- a/stereo-test/index.html +++ b/stereo-test/index.html @@ -748,6 +748,6 @@

Legal

- + diff --git a/terms-of-service/index.html b/terms-of-service/index.html index e08246a..d50c162 100644 --- a/terms-of-service/index.html +++ b/terms-of-service/index.html @@ -768,7 +768,7 @@

Legal

- + diff --git a/vibration/index.html b/vibration/index.html index a90f12f..1cec374 100644 --- a/vibration/index.html +++ b/vibration/index.html @@ -806,7 +806,7 @@

Legal

- +