◀ Back to contents

Phonalyser in a browser

This build runs inside a browser tab. Everything a measurement needs - the generator, the oscilloscope, the FFT analyser, the sweeps - is the same code as the desktop application, but the bottom of the stack is not: a page cannot open a sound card the way a native program can. This chapter is the short list of what follows from that, and it is worth reading once before you trust a number.

It only runs on Chromium

This is not a preference, it is a hard requirement. Phonalyser.web needs Google Chrome, Microsoft Edge, Opera or Brave - any browser built on Chromium. On Firefox or Safari it does not start at all: instead of the application you get a single message saying the browser is unsupported.

The reason is that the app depends on audio interfaces only Chromium implements - frame-accurate probing of the real capture rate, and the AudioWorklet form it uses to receive samples. Rather than start and then fail in confusing ways halfway through a measurement, the app checks the engine before it builds anything and refuses cleanly.

Note the scope: this is about the whole application. Missing WebUSB costs you only the QA40x backend; the wrong browser costs you everything.

The Web Audio backend

Web Audio is the default backend and the one you get with an ordinary sound card. Capture comes in through the browser's microphone/line-in permission and playback goes out through the browser's audio output.

The app asks for the device you selected exactly, in stereo, and explicitly switches echo cancellation, noise suppression and automatic gain control off, so nothing the browser offers for voice calls is applied to your signal. If the device refuses the rate that was asked for, the app retries without the rate constraint but still pinned to that device - it never quietly substitutes a different one. Anything else surfaces as a visible error rather than a recording of the default microphone.

No bit depth, and none to report

Samples reach the page as float32, downstream of the Windows shared-mode mixer. There is no integer word whose width you could choose, which is why Preferences ▸ Audio shows Device, Sample rate and Card for Web Audio and no Bit depth control at all. It is not hidden - it does not exist on this path.

The honest consequence: a Web Audio measurement is not bit-exact. Whatever the system mixer did - a resample, a volume taper, an "enhancement" - is already in the samples before this application sees them. For a genuinely bit-exact path see below.

The rate is the device's, not the app's

The app opens the stream first, reads the rate the browser actually granted and then builds its audio graph at that rate, so the two can never disagree. What it cannot do is change the rate: that is the device's Windows shared-mode format, and it is set in Windows. The full procedure - including the fact that you must reload the tab afterwards so the device is re-opened - is on its own page: Configuring the Windows input device. Read it before your first measurement; a card left at 48 kHz analyses a band that stops at 24 kHz however long an FFT you choose.

Permission, busy devices and dead ones

The QA40x, and what WebUSB needs

The QA40x backend drives a QuantAsylum QA402 / QA403 directly over WebUSB, with no operating-system audio path in the way at all. Its row appears in the backend list only when the browser exposes WebUSB; when it does not, the row is still shown, disabled, carrying its reason - Requires WebUSB over https:// or localhost - works in Chrome; other Chromium browsers may not expose it - so its absence is explained rather than mysterious.

What has to be true before an analyzer can be used:

Only one analyzer may be attached at a time. The claim is exclusive: the app takes interface 0, so the QA402 / QA403 must not be open in the QuantAsylum software or in another tab. A claim that fails leaves the device closed rather than half-owned.

It is handed back when nothing is using it. As soon as the last consumer detaches, the analyzer is parked at its safe ranges, stopped and released. That is deliberate: because the WebUSB claim is exclusive, an idle-but-claimed analyzer would lock the vendor software and every other tab out of it for as long as this page stayed open.

The bit-exact path

Since Web Audio is downstream of the mixer, the QA40x is the only bit-exact backend on this machine: its samples cross USB as 24-bit values in 32-bit words and never enter an operating-system audio path at all. The other bit-exact option is not on this machine - it is a bench, where the desktop application opens a card in exclusive mode on your behalf.

The page a bench serves

A Phonalyser server does not only serve its sound cards - it also serves this application. Ask a bench for its address in a browser and you get a copy of Phonalyser.web that is already pointed at that bench: it dials its own server automatically, before the first device scan, so the machine you loaded it from is connected by the time you open Preferences.

That page offers the bench's backends and nothing else. There is no Web Audio row and not even the disabled QA40x row. This is honesty, not a restriction: such a page arrives over plain http:// from a machine on your network, which is not a secure context, so getUserMedia and WebUSB genuinely do not exist on it. Offering a local backend there would be offering a choice whose every open must fail, and a greyed row would only invite the question "why can I see it".

If a backend you had selected earlier is no longer offered, the page says so plainly rather than failing somewhere deep in the browser. A backend remembered from a bench, on the other hand, is never migrated away: its row is restored before its server has answered, so the device, rate and card you had set on that bench survive a reload.

The page works out that it came from a bench by asking its own address - the same scheme, host and path it was loaded from - so a server reached over https:// through a TLS reverse proxy is recognised exactly like one reached directly, and the session it opens is encrypted to match. A bench that answers too slowly, more than about half a second, is the one case that goes unrecognised: the page then comes up with the full local backend list, which is not wrong - an https:// page is a secure context, so Web Audio and the QA40x really do work there.

An https page cannot reach a bench on the LAN

A bench session is a plain ws:// WebSocket. A browser refuses to open one from a page it loaded over https:// - the mixed-content rule - and it refuses before anything is dialled, so there is no timeout to interpret. The app tells you by name instead of leaving you to guess.

Loopback is exempt, so from an https:// page you can still reach a bench at localhost, 127.0.0.1 or ::1 - but not one anywhere else on the network. There is no setting that changes this. The supported route is the one above: load the application from the bench itself, where the page and the session share the same plain http:// origin and the rule never applies.

Saving and opening files

On the ordinary https:// page in Chrome or Edge, Save opens a real Save-as dialog and the file goes where you put it. On a page served by a bench - which arrives over plain http:// - that dialog does not exist. The browser downloads the file to your download folder under the suggested name instead, without asking, and the app reports it saved as soon as the download starts rather than when it finishes. If a file of that name is already there the browser quietly writes name (1).wav, and the app cannot see that it did.

Opening files behaves the same way on both kinds of page: you get a file chooser, cancelling really cancels, and the name you picked is the name that is used.

The one thing that does not work on a bench-served page is the oscilloscope's record forward to disk save - any duration longer than the capture buffer, or a save started while the scope is stopped. Recording in real time needs a genuine open file to stream into, which that page cannot obtain, so the app says so and stops rather than recording nothing.
On a bench-served page the 📂 Choose target file button in the generator's and the oscilloscope's Save to... tabs opens no picker: it simply fills the read-only name field in. That is the download path working as intended, not a dead button - there is no second press needed.

Where your settings live

Settings and card profiles are kept in the browser's local storage, not in files on disk. Two things follow. They are per browser and per site: clearing this site's data discards them, and they do not travel to another browser or another machine. And a stored value that will not parse is never silently replaced - it is copied aside under the same key with a .corrupt suffix and the run starts from defaults, so a damaged document can still be read back out with the browser's developer tools instead of being overwritten by the next save.

Both documents can be opened and edited as text from Tools ▸ Preferences / Devices - see Editing the settings as JSON. That is also how you copy a calibration to another browser or another machine, which matters here precisely because browser storage does not travel: open the store, copy the document, paste it into the same window on the other machine.

Reloading the tab is safe. On reload or close the app stops capture and playback and flushes your preferences first, so nothing in progress is orphaned and no calibration is lost. A reload is also exactly what the Windows rate procedure asks for, so it is a normal thing to do rather than something to fear.