Weekend outcome: Saw @thorstenball challenge devs to make OpenSimplex noise dance in the terminal in his Joy & Curiosity newsletter. I went further colour palettes, character sets and a tiny audio engine. Here’s what popped out.
Demo#
Ingredients#
Piece | Library / Tool |
---|---|
Noise | https://deno.land/x/open_simplex_noise |
Runtime | Deno 2.x |
Colour | ANSI 256-colour escapes |
Sound | WAV buffer → afplay (macOS) |
Recorder | asciinema + ffmpeg |
How it works (quick tour)#
- Noise –
makeNoise4D()
gives a 4-D field. Feed(x,y,t,w)
wheret
increments every frame andw
drifts slowly ➜ organic motion. - Characters – map the value from
(-1,1)
into an ASCII set (░▒▓█
,.-:=+*#
, …). - Colour – same value selects a colour from the active palette (fire, ocean, ice…).
- Sound – every third frame take the noise at
(0,0,t,w)
, scale to ~220–440 Hz, write a 100 ms WAV, pipe toafplay
, delete file. - Controls –
←/→
speed •↑/↓
zoom •c/v
palette •z/x
charset •s/l
presets •q
quit.
Key Code Snippets#
The Core Animation Loop#
function animate() {
clearScreen();
for (let y = 0; y < height; y++) {
let row = "";
for (let x = 0; x < width; x++) {
const n = noise(x / zoomX, y / zoomY, t, w);
row += getColor(n) + getChar(n);
}
console.log(row);
}
printHUD();
// Audio generation every 3rd frame
if (frameCount++ % 3 === 0) {
const toneNoise = noise(0, 0, t, w);
const freq = 220 + toneNoise * 220; // 220-440 Hz range
playTone(freq).catch(() => {});
}
t += speed;
w += 0.01;
}
WAV Generation Magic#
function generateWavData(freq: number, durationMs = 100): Uint8Array {
const sampleRate = 44100;
const numSamples = (durationMs / 1000) * sampleRate;
const bytesPerSample = 2;
const buffer = new ArrayBuffer(44 + numSamples * bytesPerSample);
const view = new DataView(buffer);
// WAV Header (44 bytes)
writeStr(0, "RIFF");
view.setUint32(4, 36 + numSamples * bytesPerSample, true);
writeStr(8, "WAVE");
writeStr(12, "fmt ");
view.setUint32(16, 16, true); // Subchunk1Size
view.setUint16(20, 1, true); // PCM format
view.setUint16(22, 1, true); // mono
view.setUint32(24, sampleRate, true);
// Generate sine wave samples
for (let i = 0; i < numSamples; i++) {
const t = i / sampleRate;
const sample = Math.sin(2 * Math.PI * freq * t);
const intSample = Math.floor(sample * 32767);
view.setInt16(44 + i * 2, intSample, true);
}
return new Uint8Array(buffer);
}
Color & Character Mapping#
function getColor(n: number): string {
const colors = palettes[currentPalette];
const i = Math.floor((n + 1) / 2 * (colors.length - 1));
return colors[Math.min(i, colors.length - 1)];
}
function getChar(n: number): string {
const charset = charsets[currentCharset];
const i = Math.floor((n + 1) / 2 * (charset.length - 1));
return charset[Math.min(i, charset.length - 1)];
}
Full repo: https://github.com/rockey5520/open_simplex_noise
Coding approach / file breakdown#
Goal: keep everything hack-friendly no external build, pure Deno.
open_simplex_noise/
├─ noise_ascii.ts # main loop: render, input, audio trigger
├─ sound.ts # minimal WAV writer + platform player
└─ preset.json # saved via `s` hotkey
Key decisions#
Concern | Choice & why |
---|---|
Runtime | Deno 2.x easy URL imports, single file run. |
Raw input | Deno.stdin.setRaw(true) to capture arrow keys without waiting for Enter. |
Animation loop | setInterval(animate, 50) gives ~20 FPS; plenty for ANSI. |
Noise sampling | (x/zoomX, y/zoomY, t, w) keeps code readable; tweak zoom at runtime. |
WAV generation | Hand-write header (44 bytes) + 16-bit mono samples; no deps. |
Temp files | Deno.makeTempFile() ➜ play ➜ delete; avoids lingering files. |
Platform audio | afplay for macOS, aplay /paplay if Linux; swap one line. |
Run it locally#
# macOS
deno run --allow-run --allow-read --allow-write noise_ascii.ts
(Linux: edit sound.ts
, replace afplay
with aplay
or paplay
)
What’s next (maybe)#
- Port to Canvas + Web Audio.
- More palettes: Added 7 total (fire, ocean, sunset, neon, forest, sandstorm, ice)
- More charsets: Added 6 total (classic, blocks, lines, bars, wide, symbols)
Credits & shout-outs#
- Prompt by @thorstenball in Joy & Curiosity #46 – cheers! ✨
- Noise lib by @joshforisha
If you remix this, tag me (@RakeshMothukuri) always keen to see odd hacks.