webdevpuneet.com

Typewriter Status Log Loader — Character-by-Character Build Log in HTML CSS JS

Typewriter Status Log Loader · Loaders · Plain HTML, CSS & JS · Live preview

CategoryLoaders

Copy & paste

Source Code

<div class="tl-console">
  <div class="tl-titlebar"><span class="tl-dot r"></span><span class="tl-dot y"></span><span class="tl-dot g"></span><span class="tl-title">build.log</span></div>
  <div class="tl-log" id="tlLog"></div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Real per-character typing
A genuine setInterval reveals one character per tick — not a CSS steps() trick.
Sequential line reveal
Each line fully completes before the next begins typing.
Blinking block cursor
A CSS-animated cursor follows the actively typing line.
Console-styled shell
A mock titlebar with window dots and a monospace log area.
Syntax-style coloring
Command prompts and success lines get distinct colors.
Data-driven log
A single LOG array of entries drives the whole sequence.
Dynamic-length safe
Works for any line length or content, since it types real string data.
Zero dependencies
Pure DOM manipulation and vanilla JS — no terminal library.

About this UI Snippet

Typewriter Status Log Loader — A Build-Console Log That Types Itself Out

Screenshot of the Typewriter Status Log Loader snippet rendered live

Some loading states are best communicated as a literal log — the terminal-style scroll of "Compiling assets…", "Optimizing images…", "Done." that CI tools and build systems show while they work. This snippet renders exactly that: a mock console window where each status line reveals itself character-by-character before the next line begins, using a real per-character interval rather than a CSS fade or a steps()-based faux-typewriter trick.

Real character-by-character typing

typeLine() uses a genuine setInterval ticking every CHAR_MS (18ms) that increments an index i and sets textSpan.textContent = full.slice(0, i) — each tick reveals exactly one more character of the actual string. This is meaningfully different from the common CSS steps() typewriter trick, which only works for a fixed-width, fixed-content <span> known at build time; because this is driven by a real loop reading real string data, log lines can be any length, generated dynamically, or even streamed in from a server one line at a time.

One line finishes before the next starts

runLog(index) types the current entry, and only once its interval clears (the full string has been revealed) does it wait LINE_GAP_MS and then recursively call runLog(index + 1) for the next line — a genuine sequential reveal, not several lines fading in on staggered but independent timers. This mirrors how a real build log actually streams: each line completes before the next begins.

Distinct from a "thinking" indicator

This is deliberately literal and technical — monospace font, a mock console titlebar with the classic red/yellow/green window dots, a blinking block cursor, and syntax-style coloring (a blue command prompt, green success line) — the register of a CI/deploy log, not an AI "thinking…" indicator like the shimmering, vague status text of an ai thinking loader. The messages here are concrete completed actions ("Bundling modules (214 files)…"), not open-ended reasoning narration.

Data-driven and extensible

The whole log comes from one LOG array of { text, tag?, ok? } entries, so changing the script is editing a list — swap in your real build/deploy/import steps, or drive entries in dynamically as a long-running task actually completes each stage. Pair it with a top loading bar for an overall progress cue alongside the detailed log, or a loading overlay as the surrounding shell.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA mock console window renders and the first log line begins typing.
  2. 2
    Watch each line type outCharacters appear one at a time with a blinking cursor, then the next line begins.
  3. 3
    Reach the final lineThe last line types out in green, signaling completion.
  4. 4
    Edit the LOG arrayChange, add, or remove { text, tag, ok } entries for your real process's steps.
  5. 5
    Tune the typing speedAdjust CHAR_MS (per-character delay) and LINE_GAP_MS (between-line pause).
  6. 6
    Drive it from real eventsCall typeLine() for each step as your actual task completes it, instead of a fixed array.

Real-world uses

Common Use Cases

Build and deploy loading screens
Show a CI-style log typed character by character, in a mock window with title bar dots and a monospace output area.
Import and export narration
Narrate parsing, validating and writing steps, each line finishing completely before the next begins to type.
Account setup screens
Make a setting up your account wait feel purposeful, with a blinking block cursor following the line currently being typed.
AI and agent task logs
Show discrete completed actions, pairing with an AI thinking loader when the wait has both a thinking and a doing phase.
Terminal-style web installers
Mimic a command-line experience for developer tools, using a genuine setInterval that reveals one character per tick.
Related: Full-Screen Percentage Counter Loader
See the Full-Screen Percentage Counter Loader for a related loaders pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

A CSS steps() typewriter animates a fixed-width element's clip or width in discrete steps, which only works cleanly for a single line of known, fixed-length text set at build time. This snippet instead uses a real setInterval that reveals one character of an actual string per tick, so lines can be any length, generated dynamically, added to an array at runtime, or even streamed in from a live source — CSS steps() can't do any of that.

runLog(index) only calls itself for the next index inside typeLine's onDone callback, which fires after the interval has revealed every character and a short LINE_GAP_MS pause has elapsed. This produces a genuinely sequential reveal — one line completing triggers the next — matching how a real build log actually streams output, rather than several independent fade-in timers that might overlap.

CHAR_MS controls the delay between each revealed character (lower is faster typing); LINE_GAP_MS controls the pause after a line finishes before the next one starts. Both are plain constants at the top of the script.

Call typeLine({ text: 'Your real status message' }, callback) directly from your task's own progress events as each stage actually completes, instead of iterating a pre-built LOG array — the typing mechanism doesn't care where the text comes from, only that you provide a string and a callback for when it should type.

Keep the setInterval-based character reveal logic in a small hook/composable that takes a string and a speed, returning the currently-revealed substring as state, updated on each tick with cleanup on unmount. Drive a list of these hook instances sequentially (starting the next only once the current one's revealed length equals its full text length) to reproduce the same one-line-at-a-time behavior declaratively.