noisy generates long-duration colored-noise audio in bounded memory and can
optionally mux it into a video whose every frame is pure black. It advances
stateful DSP generators one chunk at a time and writes each chunk immediately
with soundfile, so hours-long jobs do not require a full-duration waveform in
memory.
For the complete command reference, filter behavior, mixing model, validation rules, and long-master workflow, see the user guide.
Install the project and its test dependencies with uv:
uv sync --extra testRun the installed command with:
uv run noisy --helpTo run it directly from this checkout without installing it into the project environment, use uvx:
uvx --from . noisy --helpFFmpeg is required only for --video-output. It must be installed separately
and available as ffmpeg on PATH. On macOS, Homebrew provides it with
brew install ffmpeg; on Debian/Ubuntu, use the distribution package manager.
Audio only, with a reproducible ten-hour mono FLAC master:
uv run noisy \
--mix pink=70,brown=30 \
--hours 10 \
--sample-rate 48000 \
--channels 1 \
--target-rms-db -18 \
--seed 1234 \
--audio-output pink_brown.flacAudio plus a compatibility-oriented MP4 copy:
uv run noisy \
--mix brown=60,pink=30,white=10 \
--hours 10 \
--sample-rate 48000 \
--channels 1 \
--target-rms-db -18 \
--audio-output mixed_noise.flac \
--video-output mixed_noise_black.mp4An MKV output uses H.264 video and FLAC audio by default, preserving a lossless audio track:
uv run noisy \
--mix brown=100 \
--hours 1 \
--audio-output brown.flac \
--video-output brown_black.mkvUse --fps 30 for a conventional frame rate, or leave the default at 1 fps.
Because the picture is mathematically identical black on every frame, 1 fps is
enough for a visually identical result and keeps the video encode lightweight.
The resolution defaults to 1920x1080 and can be changed with, for example,
--resolution 1280x720.
These black-screen noise videos were generated with scripts/generate-youtube.sh
and are published on YouTube.
| Video | Duration | Watch |
|---|---|---|
| Warm Pink Noise for Deep Relaxation & Sleep | 10 Hours, Black Screen | 10 hours | Watch on YouTube |
| Pink and Brown Noise for Sleep & Sound Masking | 10 Hours, Black Screen | 10 hours | Watch on YouTube |
| Pink Noise for Sleep, Relaxation & Focus | 10 Hours, Black Screen | 10 hours | Watch on YouTube |
| Soft White Noise for Sleep & Sound Masking | 10 Hours, Black Screen | 10 hours | Watch on YouTube |
| Deep Brown Noise for Sleep, Focus & Relaxation | 10 Hours, Black Screen | 10 hours | Watch on YouTube |
See the upload guide for titles, descriptions,
and tags; the master guide for generation and
pre-upload QA; and scripts/generate-youtube.sh to regenerate them.
The color describes how power is distributed over frequency, not a timbral label for a particular instrument:
| Color | Approximate power spectrum | Intuition |
|---|---|---|
| White | constant | equal power per Hz |
| Pink | 1/f | about -3 dB per octave |
| Brown / red | 1/f² | about -6 dB per octave |
| Blue | f | about +3 dB per octave |
| Violet | f² | about +6 dB per octave |
White noise uses NumPy's modern default_rng. Pink noise uses a stateful
parallel IIR pinking filter. Brown noise uses a bounded (leaky) integrator
followed by a configurable 12 Hz default high-pass to control random-walk
drift, DC, and subsonic energy. Blue noise uses a stable finite-order
fractional differentiator when SciPy is available, and violet noise uses a
stateful first difference. Every generator retains its filter state between
blocks, including separate state and random streams for left and right stereo
channels.
Mix syntax is a comma-separated list of non-negative weights:
brown=70,pink=30
brown=7,pink=3
brown=0.7,pink=0.3
These forms are equivalent. The weights are normalized internally and treated as approximate power contributions. Each source is first scaled to a common unit-RMS reference, then receives an amplitude gain of:
gain = sqrt(normalized_power_ratio)
Thus brown=70,pink=30 uses approximately 70% brown power and 30% pink power
before the final master stage. Directly using 0.7 * brown + 0.3 * pink
would instead specify amplitude weights and would not have those power
semantics.
The final mix receives one fixed master gain derived from --target-rms-db.
A smooth ceiling limiter protects against random peaks without recalculating a
new gain for every block. The default target is -18 dBFS RMS and the default
ceiling is 0.98 linear (-0.18 dBFS).
The CLI applies a streaming output high-pass and low-pass after mixing and
before the fixed master gain and peak protection. At 48 kHz, omitted cutoffs
default to 20 Hz and 17 kHz. The low-pass default is reduced automatically to
45% of the selected sample rate when that is lower than 17 kHz. Pass an
explicit 0 to either CLI option to disable that side of the output filter:
uv run noisy --mix pink=100 --hours 1 \
--highpass-hz 0 --lowpass-hz 12000 --audio-output pink-wide.flacThis CLI behavior is intentionally different from direct library use:
AudioConfig.output_highpass_hz and AudioConfig.output_lowpass_hz default to
None, and None disables that filter. Library cutoff values must be positive,
below Nyquist, and high-pass must be lower than low-pass when both are set.
The separate brown-noise drift-control filter also defaults to
--brown-highpass-hz 12 and must be strictly below Nyquist; very low sample
rates may require overriding that option.
The default block is five seconds and can be changed with --chunk-seconds.
Only the current block, generator state, and writer buffers are resident in
memory; there is no full-duration waveform and no full-file FFT. A ten-hour
48 kHz recording therefore scales with the chunk size rather than with ten
hours of samples.
The preferred high-quality output is mono or stereo 48 kHz, 24-bit PCM in
FLAC. Noise compresses poorly, so FLAC may still be large, but it avoids the
classic RIFF WAV 4 GiB limit. Normal .wav output is PCM-24 and is rejected
before generation if its estimated size would exceed that limit; this tool does
not silently create an oversized broken RIFF file or claim RF64 support.
MP4 video uses H.264 (libx264), yuv420p, and AAC at 256 kbps by default for
broad container/player compatibility. MKV uses FLAC audio by default for a lossless
audio/video master. The --audio-codec option can override the video-track
codec, but the requested codec must be supported by both FFmpeg and the
selected container.
If only --video-output is supplied, the audio master is temporary and is
removed after muxing. Add --keep-audio to retain it; when no explicit
--audio-output is given, it is saved beside the video using the same stem and
a .flac suffix.
Install the test extra and run:
uv sync --extra test
uv run pytestThe tests cover parsing and power-gain semantics, deterministic seeded output, chunk continuity, duration and finite-sample guarantees, spectral slope sanity checks, WAV size protection, and FFmpeg command construction.
Implementation details for contributors live in the Python module docstrings; the user guide is the supported operational reference.
MIT; see LICENSE.
See CONTRIBUTING.md.
See CHANGELOG.md.