I recently finished a personal project: a Deltarune archive built with React and TypeScript.
The site has character pages, lore, music, stats and sprites — all styled after the game's battle screen UI. But it also has a secret layer that most people won't find unless they're patient.
I built a hidden ARG into it.
The trigger
If you stay on the Knight's page for 30 seconds without touching anything, something happens.
The implementation is simple — a setTimeout that resets on any user activity:
jslet idleTimer: ReturnType | null = null;
const resetTimer = () => {
if (idleTimer) clearTimeout(idleTimer);
idleTimer = setTimeout(triggerARG, 30000);
};
window.addEventListener("mousemove", resetTimer);
window.addEventListener("keydown", resetTimer);
resetTimer();
When the timer fires, it starts a chain of 6 phases:
flash → blackout → glitch dialogue → secret reveal → terminal breach → corrupted save file
Each phase is its own React state, with transitions between them handled by setTimeout chains and CSS animations.
The audio
I wanted the experience to feel unsettling. No libraries — everything is Web Audio API from scratch.
Static noise:
jsfunction createStaticNode(ctx: AudioContext) {
const len = ctx.sampleRate * 2;
const buf = ctx.createBuffer(1, len, ctx.sampleRate);
const data = buf.getChannelData(0);
for (let i = 0; i < len; i++) data[i] = (Math.random() * 2 - 1) * 0.18;
// loop + gain ramp
}
Heartbeat is two oscillators with exponential gain envelopes fired with a 220ms offset — simulating the lub-dub. It plays during the follower testimonies and the corrupted save file screen.
Voice blips for Gaster's dialogue are short .mp3 clips played as BufferSource nodes, one per character reveal, cut hard at 90ms for that crisp Undertale text sound.
The glitch typewriter
Gaster's lines reveal character by character. Each letter briefly flashes as a random Wingdings symbol before settling:
jsconst WDING_POOL = "◆○●■□★☆✓✗✞♥♠♣♦①②③④⑤";
// Each letter: set a random symbol, clear after 80–160ms
if (/[a-zà-ÿ]/i.test(ch)) {
const glitchCh = WDING_POOL[Math.floor(Math.random() * WDING_POOL.length)];
setSettling(s => ({ ...s, [idx]: glitchCh }));
setTimeout(() => {
setSettling(s => { const n = { ...s }; delete n[idx]; return n; });
}, 80 + Math.random() * 80);
}
The silent response
There's one topic in the Gaster encounter — "You jumped in on purpose" — where he just responds with ..., one dot at a time, 1 second apart. No voice. No skip. No interaction. You just wait.
It's three setTimeout calls and blocking the advance handler. But it felt like the most honest thing to write for that question.
Other easter eggs
Typing gaster anywhere on the page triggers a subtle screen effect
The Spamton, Queen and Noelle pages have alternate forms with different sprites, music and stats
The Knight's nameplate shows [NULL] after you've completed the ARG
Stack
React + TypeScript + Vite, pure CSS for all animations, Web Audio API for sound, deployed on Vercel.
Try it: deltarune-carchive.vercel.app
Code: github.com/raaywasdead/deltarune_c.archive
Go to the Knight's page and wait. 👀

Top comments (0)