Skip to content
Music / Production story

Hud mot hud

By Fredrik Brattén · Artist name: Ragnar Bratten

Everyday details and the things that matter most, held together by a search for closeness, weight and air.

From sound brief to Python seed and Suno Extend.

Hud mot hud artwork

Listen & watch

Hud mot hud

The player connects to Suno only when you choose to load it.

Open on Suno ↗

The idea

The sound brief puts two textures in tension: a deep, urban bass foundation and fragile, almost pastoral woodwinds. Fredrik wanted a close, life-affirming female voice and lyrics moving between ordinary domestic details and the feeling that time together matters.

The structure is part of that idea: a longer opening, a chorus before the first verse, and two instrumental breaks with a sustained, rolling weight.

Read a passage from the sound brief
“the unorthodox friction between a heavy, urban low end and fragile, almost pastoral acoustic woodwinds”

English translation of Fredrik’s sound brief.

Make the groove tangible

A short loop made the rhythmic direction concrete before the full song was developed. Working with ChatGPT, Fredrik turned the idea into a Python program: NumPy synthesises the sounds and Python’s wave module writes the audio file.

Production material

The four-bar seed

76 BPM · 4 bars · 12.63 seconds · mono WAV

The program synthesises a kick, a short woodblock-like rim sound, noise-based ticks and sliding bass tones. Deliberate timing offsets give the groove its pocket: the rim sits about 38 milliseconds behind the beat and the bass about 55 milliseconds behind, with small random variations on percussion hits.

Look inside the Python program

English comments accompany the original synthesis and timing code.

import wave
import numpy as np

# 1. Basic settings
sr = 44100
bpm = 76.0
beat_sec = 60.0 / bpm
bar_sec = beat_sec * 4
bars = 4
total_sec = bar_sec * bars
audio = np.zeros(int(total_sec * sr), dtype=np.float32)

def add_sound(buf, sound, start_sec):
    idx = int(start_sec * sr)
    if idx < 0 or idx >= len(buf):
        return
    end = min(idx + len(sound), len(buf))
    buf[idx:end] += sound[:end - idx]

# 2. Sound generators (dry sounds, no reverb; arrangement aims to leave midrange space)
def make_kick(dur=0.18):
    t = np.linspace(0, dur, int(sr * dur), endpoint=False)
    freq = 42.0 + 68.0 * np.exp(-35.0 * t)
    phase = 2 * np.pi * np.cumsum(freq) / sr
    env = np.exp(-16.0 * t)
    return (np.sin(phase) * env * 0.75).astype(np.float32)

def make_woodblock_rim(dur=0.045):
    t = np.linspace(0, dur, int(sr * dur), endpoint=False)
    click = np.sin(2 * np.pi * 1850 * t) * 0.6 + np.sin(2 * np.pi * 2920 * t) * 0.4
    env = np.exp(-85.0 * t)
    return (click * env * 0.55).astype(np.float32)

def make_palm_mute_hat(dur=0.025, amp=0.18):
    t = np.linspace(0, dur, int(sr * dur), endpoint=False)
    noise = np.random.uniform(-1, 1, len(t))
    hp_noise = np.diff(noise, prepend=0)
    env = np.exp(-120.0 * t)
    return (hp_noise * env * amp).astype(np.float32)

def make_fretless_sub(f_start, f_end, dur):
    t = np.linspace(0, dur, int(sr * dur), endpoint=False)
    freq = np.linspace(f_start, f_end, len(t))
    phase = 2 * np.pi * np.cumsum(freq) / sr
    wave_sig = np.sin(phase) * 0.85 + np.sin(2 * phase) * 0.15
    env = np.ones_like(t)
    attack = int(0.025 * sr)
    release = int(0.08 * sr)
    env[:attack] = np.linspace(0, 1, attack)
    env[-release:] = np.linspace(1, 0, release)
    return (wave_sig * env * 0.65).astype(np.float32)

# 3. Build a four-bar pocket groove with microtiming
np.random.seed(42)
swing_offset = (beat_sec / 2.0) * 0.26  # ~63% MPC-swing
rim_layback = 0.038                     # 38 ms behind the beat on beats two and four
bass_layback = 0.055                    # 55 ms behind the beat on the bass

for bar in range(bars):
    b_start = bar * bar_sec

    # Kick: beat one and a delayed syncopation
    add_sound(audio, make_kick(), b_start + 0.0)
    add_sound(audio, make_kick(), b_start + beat_sec * 2.5 + swing_offset)

    # Woodblock / rimshot: beats two and four, laid back
    for beat in [1, 3]:
        jitter = np.random.uniform(-0.004, 0.004)
        add_sound(audio, make_woodblock_rim(), b_start + beat * beat_sec + rim_layback + jitter)

    # Hi-hat / muted click: eighth notes with heavy swing
    for eighth in range(8):
        pos = b_start + eighth * (beat_sec / 2.0)
        if eighth % 2 == 1:
            pos += swing_offset
        jitter = np.random.uniform(-0.003, 0.005)
        amp = 0.22 if eighth % 2 == 1 else 0.12
        add_sound(audio, make_palm_mute_hat(amp=amp), pos + jitter)

    # Fretless-style sub-bass (D minor with glissando)
    add_sound(audio, make_fretless_sub(73.42, 73.42, beat_sec * 1.4), b_start + 0.0 + bass_layback)
    add_sound(audio, make_fretless_sub(87.31, 98.00, beat_sec * 1.1), b_start + beat_sec * 1.5 + swing_offset + bass_layback)
    if bar % 2 == 0:
        add_sound(audio, make_fretless_sub(110.00, 73.42, beat_sec * 1.2), b_start + beat_sec * 2.75 + bass_layback)
    else:
        add_sound(audio, make_fretless_sub(87.31, 55.00, beat_sec * 1.2), b_start + beat_sec * 2.75 + bass_layback)

# Normalise and save with the built-in wave module
audio = audio / np.max(np.abs(audio)) * 0.84
pcm_data = (audio * 32767).astype(np.int16)

with wave.open("hud_mot_hud_seed_76bpm.wav", "w") as wf:
    wf.setnchannels(1)
    wf.setsampwidth(2)
    wf.setframerate(sr)
    wf.writeframes(pcm_data.tobytes())

print("Done! Created 'hud_mot_hud_seed_76bpm.wav'.")

From seed
to song

The loop was uploaded to Suno as hud_mot_hud_seed_76bpm and used with Extend. Keeping the dry rhythmic pocket in the instructions gave the subsequent experiments a shared starting point.

01 / SOURCEThe Python seed

Four bars of rhythm and bass turn the sound idea into something to hear.

02 / DEVELOPMENTUpload & Extend

Suno extends the uploaded seed into longer musical ideas.

03 / PRESENTATIONListen to the song

The selected Studio export is available in the player above.

Directing the arrangement

The preparation turns a feeling into decisions about register, timing, vocal proximity and stereo width.

Give instruments a purpose

The brief gives recorder and transverse flute melodic and atmospheric roles in the opening. It introduces đàn bầu for long bends and replies in the instrumental breaks.

Build contrast into the form

Close, sparsely accompanied verses make space for the voice. The chorus direction opens outward with supporting voices. A longer intro, an early chorus and rolling breaks provide anticipation, recognition, intimacy and release.

People & tools

Creative direction
Fredrik Brattén
Artist name
Ragnar Bratten
AI collaboration
Sound and arrangement development; Python seed creation with ChatGPT.
Seed synthesis
Python, NumPy and the wave module.
Music development
Suno upload, Extend and Studio.

Music, moving images and the decisions behind them.

Explore the collection →