Atmosphere✨ Premium

Typing Sound Input

Text input with a synthetic mechanical click on each keystroke: square oscillator 300–500 Hz via Web Audio API, CSS progress bar — zero dependency, zero canvas.

Web AudioInputKeyboard

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Atmosphere has 58 effects, including 10 free. Explore the category →

3 usage examples

Hero / Landing — Newsletter sign-up with audio feedback — Typing Sound Input example 1

① Hero / Landing — Newsletter sign-up with audio feedback

WhenOn a landing page whose sole CTA is email capture — the user types their address in the hero section.
WhyThe mechanical click on each keystroke creates a premium, physical feel and increases engagement. The progress bar guides users toward the right format (character count target).
SettingsRandom frequency 300–500 Hz, 'square' wave, duration 50 ms, gain 0.02 — bar at 100% = 20 chars (ideal for a short email). Change to * 3 to target 33 chars.
Full-screen retro terminal — every keystroke sounds — Typing Sound Input example 2

② Full-screen retro terminal — every keystroke sounds

WhenIn an in-browser terminal, code editor, or command generator, the user types a command or plain text across the full viewport.
WhyAudio feedback is the natural, expected behaviour of a real terminal — it creates total immersion. This is the most flattering context for the effect: large surface, visible input, sound coherent with the environment.
SettingsFrequency 200 + Math.random() * 200 (200–400 Hz) for a deeper, vintage-typewriter timbre. Wave 'square', duration 0.05 s, gain 0.02. Bar multiplier × 2 ((input.value.length + 1) * 2) to cover ~50 chars — typical command length.
Conversion micro-interaction — Message field at checkout — Typing Sound Input example 3

③ Conversion micro-interaction — Message field at checkout

WhenAt the final step of a purchase funnel or registration, the user can add a personalization note or message.
WhyThe typing sound anchors the writing act in the physical and reduces abandonment at the last step. Progress toward 100% nudges users to complete the field before moving on.
SettingsExtend duration to 0.08 s and gain to 0.03 for a slightly fuller, more deliberate sound. Bar color: replace #6366f1 with your brand primary.

How it works

On each keydown event, a square OscillatorNode is instantiated through the Web Audio API. The frequency is drawn randomly between 300 and 500 Hz (300 + Math.random() * 200) to vary the timbre slightly between consecutive keystrokes, mimicking the variability of a real keyboard. The total sound duration is 50 ms.

The volume follows an exponential envelope: the GainNode starts at 0.02, then exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.05) brings it near-silence in 50 ms. This profile — instant attack, fast decay — produces the percussive click of a mechanical key without lingering resonance. The oscillator is stopped and released automatically at t+50 ms (stop()), with no memory leak.

The AudioContext is created as a lazy singleton (getSndCtx()) with a webkitAudioContext fallback for Safari ≤ 14. It stays suspended until the first keydown — compliant with the user gesture requirement enforced by Chrome 71+ and Safari 12.1+.

The progress bar (.snd-typing-bar) is a <div> whose width is recalculated in pure CSS on each keystroke: Math.min(100, (input.value.length + 1) × 5) + '%'. It reaches 100% at 20 characters. The CSS property transition: width .1s smooths the animation without RAF or extra JS.

Accessibility

  • prefers-reduced-motion: absent from the code. The CSS bar (transition: width .1s) is not suppressed when the OS enables reduced motion — add @media (prefers-reduced-motion: reduce) { .snd-typing-bar { transition: none } } to comply with WCAG 2.3.3.
  • Sound is not triggered automatically: the AudioContext only starts after the first keypress. No unexpected noise on page load — compliant with WCAG 1.4.2 (audio control).
  • The <input> is natively keyboard-focusable via Tab. However, no <label> is associated with it in the provided code — only the placeholder acts as a label, which is a screen-reader gap (WCAG 1.3.1). Add a <label for="sndTypingInput"> or aria-label in integration.
  • The .snd-typing-indicator bar carries no role="progressbar", aria-valuenow, or aria-valuemax — progress is not communicated to screen readers. Add these attributes if the metric carries semantic value for the user.
  • Text contrast: the input on #0a0a0f background with #fff color reaches ≥ 15:1 (WCAG AA and AAA). The placeholder at ~70% white opacity sits around 10:1 — still AA-compliant.

Browser compatibility

Requires Web Audio API — available in all modern browsers since 2014. Zero external dependency, zero canvas.

Chrome 66+✓ Full
Firefox 76+✓ Full
Safari 14+✓ Full
Edge 79+✓ Full
Mobile iOS✓ Gesture req.
Android Chrome✓ Full

If both AudioContext and webkitAudioContext are absent (IE 11, Opera Mini), playTone throws silently (the oscillator creation block is never reached). The text input and progress bar remain fully functional — only the sound is missing.

The code

HTML structure to paste into your page (the CSS + JS come with Premium):

index.html — structure
<div class="snd-typing-container">
  <input
    type="text"
    class="snd-typing-input"
    id="sndTypingInput"
    placeholder="Tapez quelque chose..."
  >
  <div class="snd-typing-indicator">
    <div class="snd-typing-bar" id="sndTypingBar"></div>
  </div>
</div>
Unlock the full code — $9.90/mo incl. tax

The full HTML + CSS + JS of Typing Sound Input, ready to paste — plus all 840 effects. Or $299 once, lifetime access.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Frequency range 300–500 Hz In the keydown listener, edit 300 + Math.random() * 200. Example: 200 + Math.random() * 100 gives a deeper sound (vintage typewriter); 500 + Math.random() * 300 gives a higher pitch (chiclet keyboard).
Oscillator wave type 'square' 3rd argument of playTone(freq, 0.05, 'square', 0.02). 'sine' = soft and round, 'triangle' = neutral, 'sawtooth' = more electronic and bright. 'square' is closest to a mechanical relay timbre.
Sound duration 0.05 s (50 ms) 2nd argument of playTone. 0.05 = dry key click, 0.12 = light piano note, 0.20 = perceptible organ timbre. Drop below 0.03 for a nearly imperceptible tick.
Volume (initial gain) 0.02 4th argument of playTone. 0.02 = subtle (ambient), 0.05 = present, 0.10 = loud. Stay under 0.08 to avoid startling users with headphones.
Bar color #6366f1 CSS background property of .snd-typing-bar. Replace with your brand primary. Gradients are supported: linear-gradient(90deg, #6366f1, #ec4899).
Bar fill rate × 5 (100% at 20 chars) In Math.min(100, (input.value.length + 1) * 5), adjust the multiplier: * 3 → 100% at 33 chars (long email), * 10 → 100% at 10 chars (PIN code).
CSS transition speed 0.1 s The transition: width .1s property on .snd-typing-bar. Use .3s ease-out for smoother fill, or none for instant display (and prefers-reduced-motion compliance).

FAQ

Chrome, Safari, and Firefox suspend the AudioContext until an explicit user gesture (click or keypress). The first keydown creates and resumes the context — the sound is absent only on that single keystroke. This is browser policy, not a bug. To work around it, call getSndCtx().resume() in a click handler on the page (e.g. a 'Enable sound' button) before the user starts typing.
The IIFE targets document.getElementById('sndTypingInput') at execution time — the DOM must be mounted. In React, wrap the initialization in useEffect(() => { /* run script */ }, []) or use a ref directly. In Vue, call it in the mounted() hook. For multiple instances, generate unique IDs per component (e.g. sndTypingInput-${uuid}) and add the event listener manually.
Yes. Replace the OscillatorNode with an AudioBufferSourceNode loaded via AudioContext.decodeAudioData(). Load the WAV/MP3 once at mount, store the AudioBuffer, and on each keystroke create a new AudioBufferSourceNode, connect it to the GainNode, and call start(). The listener structure stays identical — only the audio source changes.