A field of cipher noise gives way column by column as a block letter phrase decrypts out of it.
> session 0x7f3a cipher aes-256 block 1/3 Z X}C X = ; {=}:>Z=#* B>& <<$* $$A D{ E$& C*0 E!]\ F? =E X %Z * +;AB%>#=ZA> AB! \B #D<1Z :;*>}{ZX?;#*</ ! B ][;%E&; 0 /B:Y Z< ?;; 0 \ Y+#E:;F[ F%[ X{ !& ZY& &#/ D A E$; ?FF\] Y:F * ]01?:> $? ] Z+&YC} +]!D 0?\Y/DA]F&EX E* <+ F { Y#? X } 0<]; < : =<Z F0$X\; !!Y \$1B ? } ?E}$D: = D%$&?}# :{: ?1A A }X! ] < YF *1}E ZC$EX>}=D=X]/*E{; ;{#\E<{<1<} B &0 ># #; #] DA>BB<B}Z#?:A*F# ]E; !CB E? CB={Z><??BZ<111C &: %0 #E ][[ * #& }*D< ] \+! %E{} $E; { 0D* YE +:B= * AZ !>\1: A =[1!Y 01{{ A+A$}BD*0 &+D?]%=D\> *──▓=B] ;+X $ F ?*C? F 1*{[!A0} ; &;\>\E+D 1}% +1 XC!#* /<%!!F0Z =A$$ <X!! C}:E A]} Z C ▓&/<[; D>11Y BA{%E>X[ 1B E%:; [ \+ X#[A % Z> $F #\ +%F:$;B0=B;{[ \1YZ C[/$&X C{ ▓&A =D +F 0 = < X$ EE+;]D :[ &:/[+ ]= # 0$ {&? ;D 1 F<[;] !#:? A C*1=A * 1Z#* % ▓ + E%;&;]:+}1= ]=] ?\ E*<B?$0 Z&=*A %\ $/*?*E\% Z?1;C Y =BZC&]B X}];?E[X? Z + B= [ ▓$= \$\?{ C / %\ Z!YX> ! $0?>B /? > ?:BC ACB#$}=X !X] [?: FD[[ =&$%=+ ] 0$:: 1AD/ !F0 } ▓DB>+ZE*=Z\!?!F] AF0 ;+Y $!?CX X ? }>0Y&YAA Y} A? 10 F0+[} & ]\ X; B&== ] X %&?A ──▓] }0ZD <;}BB?}; \ *]/ }* %[{>!:Z [BB1 #B1 +A\?/ BX:$[>B[*Y %;]? ;+XD%A;?C1?CE E;D< +?+ #1 *\*C*;#{XY? & E A/ 0] E < { BD \+ %! X# #>0 !C $;XEC+/ }A<C>${AFD:%Z: E+FY A& ;#\ { /!E{1?F ] & /E; ! Y : <ECZ?#Z/Z0D{Y{Y\ :D*$#& A:/ !D0 BEF;C *{!;\%[ A]] 1 &#?[ [:B1%! AB\Y;A >\ #+?C+ #[B &/ ZAB * F 01 Z:&#CE$ %#?[?D ;/ AB== * \F1D;&< ;\ /# !XF1 } }%& =# * :{}Y# X}{> &>? Y/C + = ;B0?CBE%* : {$$C]< #F=X ] $ *A>{]*:<0{ &D]F}:&\%B; & ;X* E* {*C =] :}:[\1Z =Z0+\X* != B%$ Z< C /]?0!Y#!#:! ]D?+# EY FX FA > #;;0Y #! Z< + #\X +{#$$\: Y<!BF A ]X Z?*% = ]/; \Z =&?EY = !+ E$A* A%CB:AAE CF1]C ] [Z* $ decrypting [░░░░░░░░░░░░░░░░░░░░] 1%
Paste two lines into any page, play it in a terminal, or put a GIF in a README. The code follows the look you pick above.
<script type="module" src="https://www.ascii-magic.com/ascii/ascii.js"></script>
<ascii-art piece="decode-reveal"></ascii-art>// copy decode-reveal.js and kit.js into your project, then:
import { useEffect, useRef } from "react";
import create from "./decode-reveal.js";
export function Piece() {
const ref = useRef(null);
useEffect(() => {
const draw = create(), t0 = performance.now();
let raf;
const tick = () => {
const f = draw((performance.now() - t0) / 1000);
ref.current.textContent = f.text ?? f;
raf = requestAnimationFrame(tick);
};
tick();
return () => cancelAnimationFrame(raf);
}, []);
return <pre ref={ref} style={{ lineHeight: 1, fontFamily: "Menlo, Consolas, monospace" }} />;
}npx --yes https://www.ascii-magic.com/ascii/cli.tgz decode-reveal<!-- Download GIF below, commit it, then: -->
<p align="center">
<img src="assets/decode-reveal.gif" alt="Decode Reveal, animated ASCII art" width="720">
</p>Pause on the frame you like, pick a look, then download it at an exact size.
MIT licensed. It imports one small helper file, kit.js.
/* decode-reveal: a phrase decrypting column by column out of a field of cipher noise. */
import { Grid, palette } from "../lib/kit.js";
/* 5x5 block font, drawn fresh for this piece. */
const FONT = {
A: " ### |# #|#####|# #|# #", B: "#### |# #|#### |# #|#### ", C: " ####|# |# |# | ####",
D: "#### |# #|# #|# #|#### ", E: "#####|# |#### |# |#####", F: "#####|# |#### |# |# ",
G: " ####|# |# ##|# #| ####", H: "# #|# #|#####|# #|# #", I: "#####| # | # | # |#####",
J: " ###| #| #|# #| ### ", K: "# #|# # |### |# # |# #", L: "# |# |# |# |#####",
M: "# #|## ##|# # #|# #|# #", N: "# #|## #|# # #|# ##|# #", O: " ### |# #|# #|# #| ### ",
P: "#### |# #|#### |# |# ", Q: " ### |# #|# # #|# # | ## #", R: "#### |# #|#### |# # |# #",
S: " ####|# | ### | #|#### ", T: "#####| # | # | # | # ", U: "# #|# #|# #|# #| ### ",
V: "# #|# #|# #| # # | # ", W: "# #|# #|# # #|## ##|# #", X: "# #| # # | # | # # |# #",
Y: "# #| # # | # | # | # ", Z: "#####| # | # | # |#####",
0: " ### |# #|# #|# #| ### ", 1: " # | ## | # | # | ### ", 2: " ### |# #| ## | # |#####",
3: "#### | #| ### | #|#### ", 4: "# #|# #|#####| #| #", 5: "#####|# |#### | #|#### ",
6: " ### |# |#### |# #| ### ", 7: "#####| #| # | # | # ", 8: " ### |# #| ### |# #| ### ",
9: " ### |# #| ####| #| ### ", "!": " # | # | # | | # ", "?": " ### |# #| ## | | # ",
".": " | | | | # ", ",": " | | | # | # ", ":": " | # | | # | ",
"-": " | | ### | | ", "#": " # # |#####| # # |#####| # # ", ">": "# | # | # | # |# ",
"'": " # | # | | | ", " ": " | | | | ",
};
const GL = {};
for (const k in FONT) GL[k] = FONT[k].split("|");
const glyph = (c) => GL[c] || GL[" "];
export const meta = {
slug: "decode-reveal",
name: "Decode Reveal",
category: "lettering",
note: "A field of cipher noise gives way column by column as a block letter phrase decrypts out of it.",
tags: ["decrypt", "hacker", "text", "hero", "terminal"],
cols: 90, rows: 22, fps: 24,
palette: palette(["#0f2e1c", "#1a4a2c", "#2c7a46", "#7ee787", "#e9ffe9", "#8b949e", "#30363d", "#ffa657"]),
};
const PHRASES = ["ACCESS GRANTED", "HELLO WORLD", "KEY ACCEPTED"];
const NOISE = "01#%&*+=<>/\\[]{}$?!:;ABCDEFXYZ";
const PER = 7.5;
const hash = (a, b) => { let h = (a * 374761393 + b * 668265263) | 0; h = Math.imul(h ^ (h >>> 13), 1274126177); return ((h ^ (h >>> 16)) >>> 0) / 4294967296; };
export default function create() {
const { cols, rows } = meta;
const g = new Grid(cols, rows);
const BY0 = 7, BY1 = 13; // band rows that hold the phrase (glyph rows 8..12)
const masks = PHRASES.map((p) => {
const m = new Uint8Array(cols * rows);
const x0 = Math.floor((cols - (p.length * 6 - 1)) / 2);
[...p].forEach((c, k) => {
const gl = glyph(c);
for (let j = 0; j < 5; j++) for (let i = 0; i < 5; i++) if (gl[j][i] === "#") m[x0 + k * 6 + i + (BY0 + 1 + j) * cols] = 1;
});
return { m, x0, x1: x0 + p.length * 6 - 1 };
});
return (t) => {
g.clear();
const cyc = PHRASES.length * PER, tt = ((t % cyc) + cyc) % cyc;
const pi = Math.floor(tt / PER), lt = tt - pi * PER;
const { m, x0, x1 } = masks[pi];
const span = x1 - x0 + 1;
const front = x0 - 2 + ((lt - 0.4) / 3.2) * (span + 4); // reveal front, left to right
const back = lt > 6.2 ? x1 + 2 - ((lt - 6.2) / 1.0) * (span + 4) : Infinity; // re-encrypt, right to left
const step = Math.floor(t * 14);
for (let y = 1; y < rows - 2; y++) for (let x = 0; x < cols; x++) {
const i = x + y * cols;
const inBand = y >= BY0 && y <= BY1 && x >= x0 - 2 && x <= x1 + 2;
const open = inBand && x < front && x < back;
if (open) {
if (m[i]) g.set(x, y, "█", Math.abs(x - front) < 1.5 ? "4" : "3");
else if (y === BY0 || y === BY1) g.set(x, y, "─", "6");
continue;
}
const h = hash(i, Math.floor(step * (0.3 + hash(i, 1) * 0.7)));
if (h < 0.28) continue;
const near = inBand ? 1 : 0;
const col = Math.abs(x - front) < 1.5 && inBand ? "4" : h > 0.93 ? "3" : h > 0.7 || near ? "2" : h > 0.45 ? "1" : "0";
g.set(x, y, NOISE[Math.floor(hash(i * 7, step) * NOISE.length)], col);
}
// the bright scan column
if (front > x0 - 2 && front < x1 + 2) for (let y = BY0; y <= BY1; y++) g.set(Math.floor(front), y, "▓", "4");
// status line
const p = Math.max(0, Math.min(1, (front - x0) / span));
const k = Math.round(p * 20);
g.text(2, 0, `> session 0x${(0x7f3a + pi * 4111).toString(16)} cipher aes-256 block ${pi + 1}/${PHRASES.length}`, "5");
g.text(2, rows - 1, "decrypting [", "5");
g.text(14, rows - 1, "█".repeat(k), "3");
g.text(14 + k, rows - 1, "░".repeat(20 - k), "6");
g.text(34, rows - 1, "] " + String(Math.round(p * 100)).padStart(3) + "%", "5");
if (p >= 1 && back === Infinity) g.text(42, rows - 1, " OK " + PHRASES[pi].toLowerCase(), "7");
return g.frame();
};
}
/YOUR OWN
The pieces here are hand-built animations. To turn your own photo, logo or video into animated ASCII, use the ASCII Magic editor.