Praat and It's the Same but It Runs In Your Browser and Is Worse So It's Not https://braat.app
  • TypeScript 98.6%
  • CSS 0.5%
  • HTML 0.5%
  • Python 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Jocelyn Stericker 5fc8440cca
All checks were successful
/ check (push) Successful in 54s
/ deploy (push) Successful in 32s
Exclude vendor/ from vitest
2026-07-22 17:07:31 -04:00
.devcontainer build: add devcontainer wrapping the Nix flake devshell 2026-07-02 20:51:44 +01:00
.forgejo/workflows Remove references to codeberg in favour of slop.nettek.ca 2026-07-22 09:17:29 -04:00
media Add audio samples for practice route 2026-06-21 22:56:17 -04:00
mem Initial clean-up from generative model experiments 2026-07-08 07:29:45 -04:00
public Save, play, view, and analyze journal entries using the File System Access API 2026-06-26 14:42:09 -04:00
src British accents! 2026-07-22 17:07:31 -04:00
tools British accents! 2026-07-22 17:07:31 -04:00
vendor Remove references to codeberg in favour of slop.nettek.ca 2026-07-22 09:17:29 -04:00
.cta.json Initial exploration 2026-05-14 16:41:46 -04:00
.envrc build: add direnv .envrc for auto-loading the devshell 2026-07-02 20:51:44 +01:00
.gitignore build: add direnv .envrc for auto-loading the devshell 2026-07-02 20:51:44 +01:00
.gitmodules Import ort-silero-vad-wasm-minimal 2026-07-22 07:38:51 -04:00
.npmrc Remove references to codeberg in favour of slop.nettek.ca 2026-07-22 09:17:29 -04:00
.oxfmtrc.json Lint 2026-07-22 07:28:36 -04:00
.oxlintrc.json Import ort-silero-vad-wasm-minimal 2026-07-22 07:38:51 -04:00
components.json Initial exploration 2026-05-14 16:41:46 -04:00
flake.lock build: add Nix devshell with Node 22 and just 2026-07-02 20:51:44 +01:00
flake.nix build: add Nix devshell with Node 22 and just 2026-07-02 20:51:44 +01:00
index.html Add metadata for previews! 2026-05-26 17:48:58 -04:00
ipa.html Update screenshots 2026-06-17 07:59:37 -04:00
journal.html Save, play, view, and analyze journal entries using the File System Access API 2026-06-26 14:42:09 -04:00
justfile Initial clean-up from generative model experiments 2026-07-08 07:29:45 -04:00
LICENSE Initial exploration 2026-05-14 16:41:46 -04:00
package-lock.json Remove references to codeberg in favour of slop.nettek.ca 2026-07-22 09:17:29 -04:00
package.json Remove references to codeberg in favour of slop.nettek.ca 2026-07-22 09:17:29 -04:00
practice.html Update screenshots 2026-06-17 07:59:37 -04:00
privacy.html Initial clean-up from generative model experiments 2026-07-08 07:29:45 -04:00
README.md lint 2026-07-22 09:21:53 -04:00
tsconfig.json tsconfig: exclude vendor from tsc 2026-07-22 13:40:37 -04:00
vite.config.ts Initial clean-up from generative model experiments 2026-07-08 07:29:45 -04:00
vitest.config.ts Exclude vendor/ from vitest 2026-07-22 17:07:31 -04:00

Braat

What is Braat?

Braat shows you, in real time, the pitch and resonance of your voice as you speak. It's a practice aid aimed at voice training — including trans voice training, where pitch (F0) and vowel resonance (F1-3) are commonly used reference points. It may also be useful for singing practice, accent work, or just exploring how your voice works.

Open Braat →

Braat screenshot showing a spectrogram with pitch and formant tracks

Why Braat?

Braat's signal-processing algorithms are adapted from Praat, a widely used tool in phonetics research. Praat is primarily oriented toward offline analysis; Braat takes its algorithms and runs them on a live microphone in the browser, with no install and no upload.

Features

  • Live spectrogram — your voice's frequency content as you speak
  • Pitch (F0) tracking — Praat's filtered autocorrelation method
  • Formant (F1-F3) tracking — Burg's method LPC, plotted on a vowel chart
  • Voice activity detection — Silero VAD picks out voiced segments
  • File import — drop in a recording and analyze it offline
  • Private — audio stays in your browser; nothing is uploaded
  • Offline — once loaded, works without a network connection

Browser support

Braat uses AudioWorklet, Web Workers, and SharedArrayBuffer, which need a recent browser (Chrome, Firefox, or Safari).

Status

Braat is alpha and in active development. Core features work, but expect changes to the UI and algorithms. A usage guide is planned but not yet written.

How it works

All audio processing happens in the browser. An AudioWorklet captures microphone PCM into a SharedArrayBuffer ring, and three Web Workers read from it in parallel:

  • a spectrogram worker (FFT-based, with a Bark-scaled colormap),
  • a formant worker running pitch (filtered autocorrelation) and formant (Burg LPC) analysis,
  • and a VAD worker running Silero v6 via ONNX Runtime Web.

DSP code ported from Praat via LLM is attributed in each source file's copyright header.

Common Commands

# Development
npm run dev            # Run dev server on port 3000
npm run build          # Build for production

# Code Quality
# Prefer `npm run check` over running the underlying tools (oxlint, oxfmt)
# directly: it bundles format + lint-fix into one step.
# Note: `npm run check` or oxlint is how you typecheck, tsgo isn't installed directly.
npm run check

# Testing
npm run test           # Run tests with Vitest
npm run e2e            # Run slow end-to-end tests
npm run test -- --silent=false --disable-console-intercept  # Show logs

# Reference media
npm run media:fetch    # Mirror reference clips into media/references/ (see below)

Tech Stack

  • UI Framework: TanStack Router (React 19 SPA with file-based routing under src/routes/)
  • Styling: Tailwind CSS v4 (via @tailwindcss/vite plugin)
  • Components: shadcn/ui with Base UI (not Radix), configured via components.json ("style": "base-nova"). Components are vendored into src/components/ui/ with minor local changes on top.
  • Icons: lucide-react

Key Architectural Decisions

  1. Real-time Priority: Spectrogram, waveform, and formant data must remain responsive. Slower computations should not block visualization. If necessary, defer or make features optional rather than blocking the UI.

  2. Audio Worklet for Low-Latency: Realtime DSP runs in an AudioWorklet; UI and DSP communicate via message passing (not direct function calls).

  3. Vendored DSP Code: Algorithms are ported to TypeScript from reputable sources (primarily Praat) with clear attribution. Avoid WebAssembly when reasonable TypeScript alternatives exist.

  4. Stream & Batch Processing: Each algorithm should provide:

    • A stream wrapper (avoids array allocations during processing when possible)
    • A batch wrapper (for offline/file import processing)
  5. Browser-Only: All processing runs in the browser. The server only builds and serves static assets.

Adding UI Components

To add new shadcn/ui components (pulled from the Base UI registry configured in components.json):

npx shadcn@latest add <component-name>

Components are vendored into src/components/ui/ and can be imported directly. We carry minor local changes on top of the generated code, so review diffs before re-adding or updating a component.

Reference Media (media.braat.app)

The practice route plays ~140 MB of synthesized reference clips (per-sentence MP3s). These are not in the repo. media.braat.app holds them, along with manifest.json.

  • Generating clips: npm run synth:references, very slow
  • Fetching clips: npm run media:fetch
  • Local development: flip USE_LOCAL_MEDIA to use local references
  • URL resolution: see src/lib/mediaConfig.ts
  • Host headers: media/_headers (tracked) sets cross-origin isolation headers Grebedoc adds Content-Type, caching, and ACAO automatically.

Analytics

Anonymous, cookieless usage stats via GoatCounter, via src/lib/analytics.ts (trackPageview / track). Events have no properties, so dimensions are encoded into the event name (family/value) from a small, fixed set. See src/routes/privacy.tsx for the user-facing disclosure. Do Not Track / GPC are intentionally ignored. It's not really aiming to stop first-party, anonymous counts. See https://www.arp242.net/dnt.html.

CI/CD

CI runs on Forgejo (.forgejo/workflows/ci.yaml). The workflow lints, tests, builds, and deploys to Grebedoc on every push to main.

Forgejo supports GitHub Actions syntax, but compatibility with third-party marketplace actions is not guaranteed. Prefer runner-agnostic shell steps where possible.

Nix

You can, but do not need to, use just and nix for development. Common tasks are wrapped as just recipes (run just to list them). You can use VS Code / Codespaces to open a devcontainer, use direnv + Nix, or simply nix develop.

Contributing

The canonical source and issue tracker for Hacklily is my Forgejo instance, https://slop.nettek.ca/jocelyn-stericker/braat.

I love receiving emails from people using Braat. My preferred way of receiving suggestions, issues, or patches is by email, jocelyn@nettek.ca.

You can also:

Contributions must be licensed AGPLv3-or-later. Feel free to use AI, but please be mindful that I will need to read the patch.

License

Copyright (C) 2026 Jocelyn Stericker jocelyn@nettek.ca

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.

This project contains code derived from Praat. That code is:

Copyright (C) 1992-2008,2011,2012,2015-2020,2022-2024 Paul Boersma

Copyright (C) 1993-2020 David Weenink

This project also contains a TypeScript port of the Bournemouth Forced Aligner (BFA) for phoneme-level forced alignment, in src/lib/alignment/. The upstream BFA is:

Copyright (C) Tabahi tabahi@duck.com

The upstream BFA is licensed under GPLv3; this port is distributed under AGPL-3.0-or-later. It runs the CUPE acoustic model via onnxruntime-web; the model weights are not bundled. See src/lib/alignment/README.md and ATTRIBUTION.md for details.