Artificial Wasteland · an instrument made of your own room

The Hum Is a Clock

Somewhere in your room something is humming at twice the frequency of your electricity grid. That frequency is never exactly 50, or exactly 60. It moves, by tens of millihertz, all day, and every generator on your synchronous grid moves with it. Not your continent: the European regulation this page checks against names four separate synchronous areas, which is why it has to quote two different legal bands below.

A microphone can read it. Not the wall socket, not a meter: the sound a transformer makes. This page opens your microphone, listens for twenty seconds, and reports your grid's frequency with an interval. Before it will do that, the identical estimator has to recover four answers that are already known, in front of you, including one it must get wrong on purpose.

And then there is the catch, which is the better half of the page. What comes out is not the grid. It is the grid divided by the crystal inside your own sound hardware. This page measures that too.

First: four answers it already knows

A page that measures your room has a problem no arithmetic page has. You have no second instrument. You cannot check it. So this one is not allowed to report your number until the same code has recovered four numbers that were fixed in advance and frozen before any of them ran. Two of the four are traps: a hard-coded estimator sails through the first and dies on the rest.

The slate

rolespecimenknownmeasuredoff bytol
running…

running the slate…

The control on the control

This is not an argument that a constant would fail. It is the same slate, run again through an estimator that has been deliberately hard-coded to return the first anchor's published value with a small error bar, so you can watch it fail.

Your room

Twenty seconds, somewhere with mains power and preferably a light or a charger on. Nothing leaves your browser; the check panel at the bottom lists every network request this page has made, and there is a content security policy in the head that forbids any other origin.

Your grid, right now

not measured yet

The instrument has to arm first. That is happening above.

what this page measured a published log one standard deviation

The shipped anchor, drawn against the published log it was made from.

candidate linegrid this impliesbest in-band SNRwindows above the floor

Nothing arrived? That is a result, and it has its own section below. A battery-powered device in a room with no wiring nearby genuinely has no mains hum in it, and a page that always produces a number is a page that is not measuring.


You did not measure the grid

Here is the objection, and it is correct. Your sound hardware counts samples with a quartz crystal, and that crystal is not exactly right. It says 48000 samples per second and delivers 47997, or 48004. Every frequency this page reports is scaled by that error, because the only ruler it has is the sample count.

An ordinary consumer crystal is specified to about 100 parts per million. On a 50 Hz grid that is 5.0 mHz of error, a tenth of the 50 mHz that Continental Europe's standard frequency range allows either side of nominal, and larger than this estimator's own noise floor. A page reporting millihertz from a laptop microphone without saying so is lying.

This is not a new worry. It is a clause in a forensic guideline that is older than most phones:

Calibration/measurement should be routinely carried out on the database clock source used in the digitisation process of the ENF. A correction factor to the database ENF estimates would need to be applied for data that had been collated using a soundcard that had a bias in its sampling frequency. ENFSI Forensic Speech and Audio Analysis Working Group, Best Practice Guidelines for ENF Analysis in Forensic Authentication of Digital Evidence, ref. FSAAWG-BPM-ENF-001, Issue No. 001, 2 June 2009, clause 3.6.2

Read whose clock that is, though, because it is not yours. The guideline is telling a laboratory to calibrate the soundcard that digitised its own reference database, which is the far end of the comparison from you. Your microphone is the questioned recording's end, and no clause in that document asks anyone to calibrate it. The worry carries over exactly; the instruction does not, and this page will not borrow authority it was not given. It measures the crystal anyway, because the arithmetic does not care which end of a comparison the wrong clock is on.

The answer is not a caveat. It is three more results, and you can operate all three.

One. Your crystal, measured, right here

A browser hands you both clocks in one object. AudioContext.getOutputTimestamp() returns contextTime, which advances with the audio device's own sample counter, and performanceTime, which advances with the system clock. Regress one on the other and the slope is the ratio between two crystals. This needs no microphone and no permission at all.

Your audio clock against your system clock

not measured yet

Press the button. It listens to nothing; it just watches two clocks disagree.

This is a ratio of two crystals, not a calibration. Neither one is a standard. If your system clock is being steered by NTP while this runs, that steering lands in this number, which is why the fit residual is printed with it. And this is the one panel on the page that the browser gate cannot check. The gate feeds Chromium a file through a fake audio device, and that device is not a crystal: asked this same question it answers, but it answers with the pacing of Chromium's own sample generator, which came out near −1300 ppm here and moved by hundreds of ppm between runs. That is an order of magnitude outside what a real crystal is specified to, so there is nothing there to check the arithmetic against. The verifier measures it in that browser rather than assuming it. What is checked instead is offline: the regression, against slopes planted at known parts per million and including the sign, and the estimator's ratio behaviour, against the specimen deliberately mis-clocked below.

Two. Break the clock on purpose and watch what survives

The shipped anchor is a recording whose true answer is known exactly. Below, the page resamples that same file by a clock error you choose and runs the same estimator over the result. Watch the absolute reading move by exactly the amount the arithmetic predicts, and watch the shape of the trace not move at all.

Mis-clock the anchor

Three. The shape is the whole of forensic ENF

The forensic use of mains hum is not the absolute value. It is matching the wander of a questioned recording against an archived log, which is what ENFSI clause 5.1.4(c) describes: overlay two vectors and compute a correlation. A constant clock error moves a whole trace up or down together, and a correlation with the mean removed cannot see that. So the objection kills one claim and leaves the other standing, and this page can say which is which because it measured both.

Below, the recovered trace is matched against a shipped day of the Nordic grid log at every second of it the trace can be laid against: a three minute trace has to fit inside the day, so the count printed under the graph is a little short of its 86,400. The negative control is a real recording of a real grid on the other side of the world, which has no correct answer in this day at all.

Find this recording in the day

Correlation of the recovered trace against the shipped day, at every second. The dashed line is the 99.9th percentile of the scores at every candidate second, the true one included, so the winning peak always stands above it and standing above it settles nothing. What decides a match here is the gap between the peak and the best answer somewhere else in the same day.


When there is nothing to find

Every room does not hum. Off-grid, battery-powered, DC-lit and aggressively filtered captures have no mains component in them at all, and the dataset used here ships a set of recordings made for exactly that test: same rooms, same devices, no mains hum. A refusal on one of those is the correct answer, and it is the single hardest thing to get right, because a spectrum always has a maximum.

A real recording with no mains in it

The spectrum of the shipped null recording across all four candidate bands, so you can see the absence rather than take it on trust.

And when there are two sources

Two harmonics of one grid must give the same number. A second source (an uninterruptible power supply, an inverter, an unrelated tone) does not, and that is the case ENFSI clause 2.2(c) separates out. Press this to add a second source to the shipped anchor and watch the same estimator stop answering.


A regulation, checked

European law names the number this page measures. Commission Regulation (EU) 2017/1485 (the system operation guideline) sets, in Annex III Table 1, a standard frequency range of ±100 mHz for the Nordic synchronous area and ±50 mHz for Continental Europe, and in Table 2 a target of at most 15 000 minutes per year outside that range, the same 15 000 for all four synchronous areas. Here is that count, summed in your browser from the deposited log. Then the same log counted three other ways, because the regulation names a number of minutes and the log is one sample per second, and nothing anywhere says which operation turns one into the other. One of the four reverses the verdict.

Nordic grid, 2023, against Annex III

Article 127(6) lets the transmission system operators of a synchronous area agree different values from Table 2 in their synchronous area operational agreement, so the 15 000 above is the regulation's default and not necessarily what applies. And the deposit does not cover the whole year, which is why the comparison below it is pro rata and says so.


What this is not

Show the check

What the instrument attested, verbatim

Does the reading actually move, or is that just noise?

A drift figure on its own has no content: a handful of noisy estimates of a perfectly constant frequency also has a non-zero range. So every trace on this page is tested against its own per-window intervals, and one of the shipped specimens is constant by construction, which is what makes the test capable of saying no.

Every free choice in the estimator

Measured offline, over 1.4 gigabytes of the ENF-WHU dataset

These cannot run in a browser: they are a sweep over every H1 recording in that dataset against its own simultaneously recorded mains reference, plus the whole H0 null set. They were measured by research/enf-mains-hum/calibrate.mjs, recorded in research/enf-mains-hum/calibration.json together with the list of files it read, and the verifier asserts that what this page says matches that file. This is the one block on this page you cannot re-run yourself without downloading the dataset, which is why it says exactly what was swept, and why the failure it turned up is printed here rather than summarised.

Breaking it on purpose

Everything above this line is green, which is also exactly what a verifier looks like when it is testing nothing. So the verifier takes the same engine.js this page just ran, injects one defect at a time, and requires a named check to go red for each one. The defects this slate provably cannot see are listed too, because saying which of them survive is part of saying what the slate is worth.

EACH DEFECT IS ONE SUBSTITUTION INTO engine.js, AND THE CHECK THAT CATCHES IT

▸ the estimator returns a constant 50.000 Hz
    Every specimen except the one whose value was hard-coded, and the null recording
    emits a number where silence was the correct answer.

▸ the estimator returns the nominal it chose
    synth-60, which sits 12.9 mHz off nominal against a 12 mHz tolerance. That gap is
    the entire reason the 60 Hz control was built off nominal rather than at 60.000.

▸ the sub-bin refinement is skipped and the raw peak bin is returned
    The anchor, the 60 Hz control and the mis-clocked control all fall outside tolerance.

▸ the in-band SNR floor is removed, so nothing is ever refused for weakness
    The nominal can no longer be chosen at all: with no floor, noise ties 50 against 60.

▸ the harmonic agreement check is disabled
    Two sources at odds are averaged into 49.98511 Hz, which is neither of them: 42 mHz
    above the anchor's own answer and 8 mHz below the interferer's grid.

▸ the both-harmonics rule is dropped, so one line is enough
    A single isolated tone, which is not what a grid looks like, is reported as a grid.

▸ the sample clock ratio is inverted, so a fast clock is reported as a slow one
    A clock planted 500 ppm fast comes back as -499.750 ppm. The browser gate could
    not catch this: it never reads the ratio, and the fake audio device it drives has
    no true ratio to be read, as the panel above says and the verifier measures.
    This one is caught offline or not at all.

▸ the demonstrated accuracy floor is removed from the interval
    The interval falls to 0.29 mHz on real room audio, where 3.0 mHz is all this
    estimator has ever demonstrated against an independently recorded reference.

SURVIVES, AND IS RECORDED AS SURVIVING

▸ forcing the log-parabolic interpolation to zero
    Nothing catches it, and nothing should. The parabola is only a starting point for
    the Goertzel search, which has no bin grid at all, so removing it moves every
    specimen by under 20 nanohertz. If this one ever starts going red, the method
    described above is the thing that has gone wrong.

Sources, and what each one licenses

GRID FREQUENCY LOG
  Pre-Processed Power Grid Frequency Time Series (2020-2023), doi:10.5281/zenodo.15784548.
  Only the Fingrid (Nordic) series is used or shipped. The deposit's Zenodo licence field
  says cc-by-4.0 for the record, and the deposit's own LICENSE.md does not agree with it:
    "The pre-processed data in the subfolders `**/Fingrid` are licensed under the CC-BY 4.0."
    "TransnetBW/ Netztransparenz originally did not publish their data under an open
     license ... we cannot publish our pre-processed version under an open license due to
     the missing license of the original data."
  So the Continental Europe series is NOT redistributable and is not here in any form. This
  page is about the Nordic grid because that is the one whose licence permits it. Attribution
  as CC BY 4.0 requires: A. Nikoltchovska, S. Puetz, X. Li, V. Hagenmeyer and B. Schaefer,
  Zenodo, doi:10.5281/zenodo.15784548; original measurements Fingrid Oyj, "Frequency,
  historical data", published by Fingrid under CC BY 4.0. The full text of both licence
  findings is in this page's specimens/LICENCE.

REAL ROOM AUDIO
  ENF-WHU-Dataset, github.com/ghua-ac/ENF-WHU-Dataset. MIT License, Copyright (c) 2023
  HUA GUANG, fetched verbatim from the repository root on 2026-08-17: permission to "use,
  copy, modify, merge, publish, distribute, sublicense, and/or sell copies", conditioned only
  on the copyright and permission notice travelling with it, which it does, in
  specimens/LICENCE. The excerpts here are low-passed at 300 Hz and decimated to 1 kHz, which
  is an anti-alias band limit and is also the privacy measure: these are recordings of real
  rooms with real people in them, and a 300 Hz ceiling removes the consonant energy that
  carries word intelligibility, though it leaves pitch and rhythm. No listening panel was run
  to confirm that, and specimens/LICENCE says so. The band limiting is applied by the build
  script, not by hand.

STANDARDS AND GUIDELINES
  Commission Regulation (EU) 2017/1485 (system operation guideline), Article 127 and
  Annex III Tables 1 and 2. Read from EUR-Lex, CELEX 32017R1485, on 2026-08-17.
  ENFSI Forensic Speech and Audio Analysis Working Group, Best Practice Guidelines for ENF
  Analysis in Forensic Authentication of Digital Evidence, ref. FSAAWG-BPM-ENF-001,
  Issue No. 001, 2 June 2009, ed. C. Grigoras, A. Cooper and M. Michalek. Clauses this page
  relies on, and what each is used for:
    2.2(c)   detect the TYPE of ENF component (mains, uninterruptible power supplies, etc.),
             which is the two-source refusal above.
    3.6.2    the soundcard clock bias, quoted verbatim in the second layer.
    5.1.1(a) remove the DC component. Done once, before anything spectral.
    5.1.1(b) a band pass of +/- 0.5 to +/- 1 Hz either side of the line. That is this page's
             search band, applied at the harmonic it reads rather than at the fundamental.
             It is NOT the 300 Hz low-pass above, which is an anti-alias band limit, and
             5.1.1(c) recommends decimating to ENF x 2.4, which this page does not do because
             it has to keep 240 Hz for a 60 Hz nominal at h = 4.
    5.1.2(b) an FFT with an interpolation scheme, which is the locate-then-refine step.
    5.1.4(c) overlay two length N vectors and compute a match metric: the dating panel.
    6.3      two competing hypotheses rather than a verdict, which is why this page reports a
             comparison and stops there.
  D. C. Rife and R. R. Boorstyn, "Single-tone parameter estimation from discrete-time
  observations", IEEE Trans. Inf. Theory 20(5):591-598, 1974.
  G. Hua and H. Zhang, "ENF Signal Enhancement in Audio Recordings", IEEE Trans. Inf.
  Forensics and Security 15:1868-1878, 2020, which the ENF-WHU README asks to be cited.

Every network request this page made

Reproduce it

Run node research/enf-mains-hum/verify-enf-mains-hum.mjs. It regenerates the synthetic specimens and compares their hashes, recomputes every number above from the same engine this page runs, and checks each of them against the text you are reading. Run node scripts/check-live-sensor.mjs --only=enf-mains-hum to drive this page's own microphone path in a real browser with a signal whose answer is already known.

Built 17 August 2026. The estimator, the specimens, the offline mirror and the verifier are one implementation: public/strata/enf-mains-hum/engine.js, loaded unchanged by all four.