Architecture and design of Hush Meet
┌─────────────────────────────────────────────────────┐
│ Chrome Extension (Manifest V3) │
│ │
│ ┌──────────────┐ ┌───────────────────────────┐ │
│ │ Popup UI │ │ Content Script │ │
│ │ (React) │ │ (meet.google.com) │ │
│ │ │ │ │ │
│ │ - Toggle │ │ ┌────────────────────┐ │ │
│ │ - Meter │◄───►│ │ Audio Analyser │ │ │
│ │ - Settings │ │ │ (Web Audio API) │ │ │
│ │ │ │ └────────┬───────────┘ │ │
│ └──────────────┘ │ │ │ │
│ ▲ │ ┌────────▼───────────┐ │ │
│ │ │ │ State Machine │ │ │
│ │ │ │ IDLE → MUTED → │ │ │
│ │ │ │ SPEAKING → GRACE │ │ │
│ │ │ └────────┬───────────┘ │ │
│ │ │ │ │ │
│ │ │ ┌────────▼───────────┐ │ │
│ │ │ │ Mute Controller │ │ │
│ │ │ │ (DOM manipulation)│ │ │
│ │ │ └────────────────────┘ │ │
│ │ └───────────────────────────┘ │
│ │ │
│ └─── Chrome Storage API (config/state sync)─┘
│ │
└─────────────────────────────────────────────────────┘
src/
├── constants.ts # Constants, modes, thresholds, storage keys
├── messages.ts # i18n messages (EN/JA)
├── i18n.ts # Locale management
├── manifest.ts # Chrome extension manifest definition
├── background/
│ └── service-worker.ts # Service Worker (game launcher, etc.)
├── content/
│ └── index.ts # Content Script (state machine, mode control, shortcuts)
└── popup/
├── index.html # Popup entry HTML
├── main.tsx # React mount
├── Popup.tsx # Popup UI component
├── popup.css # Styles
├── Equalizer.tsx # Spectrum analyser
├── ThemeSwitcher.tsx # Theme switcher
├── LocaleSwitcher.tsx# Language switcher
└── GameLauncher.tsx # Mini-game launcher
src/content/index.ts)This script is injected into the Google Meet page. Its main responsibilities are:
Uses the Web Audio API AnalyserNode to measure microphone input volume in real time.
getUserMedia() → AudioContext → MediaStreamSource → AnalyserNode
│
getFloatTimeDomainData()
│
RMS (Root Mean Square) calculation
Configuration parameters:
| Parameter | Value | Description |
|---|---|---|
fftSize |
512 | FFT window size |
smoothingTimeConstant |
0.3 | Temporal smoothing coefficient |
echoCancellation |
true | Enable echo cancellation |
noiseSuppression |
false | Noise suppression off (for VAD accuracy) |
A finite state machine with 5 states manages the speech state.
IDLE ──(mode other than Off selected)──► MUTED ──(volume > speechThreshold)──► UNMUTING ──► SPEAKING
▲ │
│ (volume < silenceThreshold)
│ │
└──(graceTimer expired)── GRACE ◄────────────────┘
│
(volume > silenceThreshold)
│
▼
SPEAKING
* If the user manually toggles the Meet microphone, the mode stays the same and only the state follows.
| State | Description | Meet Action |
|---|---|---|
| IDLE | Off mode or uninitialized | None |
| MUTED | Silence detected → muted | Mute |
| UNMUTING | Speech detected → unmuting | Unmute |
| SPEAKING | Currently speaking | None |
| GRACE | Speech ended → grace period | None |
Four modes control how state transitions behave. Off is the control-disabled mode.
| Mode | Unmute | Mute | Shortcut |
|---|---|---|---|
| Off | None | None | Disabled |
| Auto | Automatic on voice detection | Automatic on silence | Toggle |
| Auto-Off | Manual / shortcut | Automatic on silence | Toggle |
| Push-to-Talk | While key is held down | Key released → Grace → Mute | Hold |
Provides a customizable keyboard shortcut (default: Ctrl+Shift+M).
The shortcut key can be freely changed from the popup settings.
Different thresholds for unmuting and muting prevent chattering (frequent toggling).
speechThreshold (speech start) = 0.025 (default)
silenceThreshold (silence) = speechThreshold × 0.5 = 0.0125
Volume
▲
│ ┄┄┄┄┄┄┄┄┄ speechThreshold (0.025) ┄┄┄┄ ← Unmute when exceeded
│
│ ┄┄┄┄┄┄┄┄┄ silenceThreshold (0.0125) ┄┄ ← Grace period starts when below
│
└──────────────────────────────────────────► Time
Detects the Google Meet mute button using multiple strategies:
aria-label attribute (supports Japanese/English)data-tooltip attributeCtrl+D keyboard shortcut
Mute state is determined from the data-is-muted attribute or the aria-label text content.
src/popup/)A settings UI built with React 19. Synchronizes state with the Content Script in real time via the Chrome Storage API.
speechThreshold (0.005–0.25) — saved per mode
gracePeriod (500ms–4000ms) — saved per mode
Popup → Chrome Storage → Content Script
│ │
│ hushMeetConfig: object │ (sensitivity/grace period settings)
│ hushMeetMode: string │ (operating mode)
│ hushMeetShortcutKey: str │ (shortcut key)
│ │
│ ▼
│ Content Script → Chrome Storage → Popup
│ │
│ │ hushMeetState: string (current state)
│ │ hushMeetLevel: number (volume level)
│ │ hushMeetSpectrum: array (spectrum)
└────────────────────────────┘
The Popup and Content Script do not communicate directly; they communicate bidirectionally via Chrome Storage API change events.
| Tool | Version | Purpose |
|---|---|---|
| Vite+ | toolchain | Dependency management, build, test, validation |
| Vite | 8.x | Internal build infrastructure |
| CRXJS Vite Plugin | 2.x | Chrome extension HMR & manifest generation |
| React | 19.x | Popup UI |
| TypeScript | 5.9.x | Type safety |
| Command | Description |
|---|---|
vp install |
Install dependencies |
vp build |
Production build |
vp test |
Run tests |
vp check |
format / lint / typecheck |