ASCII loading spinners and progress bars for websites

Lightweight animated ASCII loaders, spinners and progress bars for web pages. One tag to embed, no images to download, honours reduced motion.

Loading the builder…

/WHY

A loader is the one part of a page everyone sees while they wait, so it is a good place for some character. An ASCII loader is text, not an image, so it appears instantly, stays sharp on every screen and weighs a few kilobytes.

The builder below gives you a full-page overlay that removes itself when the page has loaded, an inline loader for a panel, or a small one in the corner, plus React and Vue components and a pure CSS spinner with no script at all.

/EXAMPLES

Ready-made examples to copy

Each one is complete. Copy it as it is, or change the piece name to any piece in the library.

Full-page loader that hides itself

Paste after <body>. It fades out on window load, but never before 400 ms, so fast pages do not flash.

<div id="ascii-loader" role="status" style="position:fixed;inset:0;z-index:9999;display:grid;place-content:center;justify-items:center;gap:16px;background:#09090b;color:#e4e4e7;transition:opacity .3s">
  <ascii-art piece="progress-loader" size="8"></ascii-art>
  <p style="margin:0;font:14px system-ui">Loading…</p>
</div>
<script>
  var t0 = Date.now();
  addEventListener("load", function () {
    setTimeout(function () {
      var el = document.getElementById("ascii-loader");
      el.style.opacity = 0; setTimeout(function () { el.remove(); }, 300);
    }, Math.max(0, 400 - (Date.now() - t0)));
  });
</script>
<script type="module" src="https://www.ascii-magic.com/ascii/ascii.js"></script>

Loading state inside a button

Swap the label for a spinner while a form submits.

<button id="save"><span class="label">Save</span></button>
<style>
  .busy .label::after { content: "⠋"; margin-left: 8px; animation: spin .8s steps(1) infinite; }
  @keyframes spin { 0%{content:"⠋"} 20%{content:"⠹"} 40%{content:"⠼"} 60%{content:"⠦"} 80%{content:"⠇"} }
</style>
<script>
  save.onclick = async () => {
    save.classList.add("busy"); save.disabled = true;
    await fetch("/api/save", { method: "POST" });
    save.classList.remove("busy"); save.disabled = false;
  };
</script>

React: Suspense fallback

Any lazy route or data component shows the loader while it loads.

import { lazy, Suspense } from "react";
import { AsciiLoader } from "./AsciiLoader";

const Dashboard = lazy(() => import("./Dashboard"));

export default function App() {
  return (
    <Suspense fallback={<AsciiLoader text="Loading your dashboard…" />}>
      <Dashboard />
    </Suspense>
  );
}

Skeleton screen

For lists and cards, a skeleton that shows the shape of the content often feels faster than a spinner.

<div style="display:grid;place-items:center;padding:40px">
  <ascii-art piece="skeleton-loader" size="7"></ascii-art>
</div>
<script type="module" src="https://www.ascii-magic.com/ascii/ascii.js"></script>

Only show it when loading is slow

Wait 300 ms before showing anything. Most loads finish first and nobody sees a flicker.

function showLoaderIfSlow(promise, el, delay = 300) {
  const timer = setTimeout(() => (el.hidden = false), delay);
  return promise.finally(() => { clearTimeout(timer); el.hidden = true; });
}

// showLoaderIfSlow(fetch("/api/data"), document.getElementById("ascii-loader"));

How to do it

  1. Pick a loader from the Interface category.
  2. Add the script tag once on the page.
  3. Show the ascii-art element while loading, and hide it when your data arrives.

/GUIDE

Which style to use

Use the full-page overlay for the first load of a single page app or a page that builds itself in the browser. Use the inline loader inside a panel, table or chart that fetches its own data while the rest of the page is usable. Use the small corner loader for background work such as saving or syncing.

Do not slow the page down

The loader script is a module, so it never blocks the page from rendering, and the piece file is a few kilobytes. Keep the loader markup in the HTML itself rather than adding it with JavaScript, so it appears with the first paint.

Set a minimum display time of 300 to 500 ms. A loader that flashes for 50 ms looks like a glitch.

Accessibility

Give the loader role="status" and a short text such as "Loading your dashboard", so screen readers announce it. The ascii-art element is hidden from screen readers and holds a still frame for anyone who prefers reduced motion.

Remove the loader from the page when loading is done rather than only hiding it, so keyboard focus can never land behind it.

Pieces that work well for this

Open any of them to copy the code or export.

/FAQS

ASCII loading spinners and progress bars for websites: frequently asked questions

Q. What is an ASCII spinner, and how do I add one to a website?
An ASCII spinner is a loading indicator drawn with text characters instead of an image. To add one, paste the builder's HTML right after the body tag: it shows a piece such as Spinner Set while the page loads, then fades out and removes itself. Each piece is only a few kilobytes and stays sharp on every screen.
Q. How do I add an ASCII loading animation to a web page?
Choose a piece and a style in the builder, then copy the version for your stack: plain HTML, React, Vue or a pure CSS spinner. The HTML version is a full-page overlay that hides itself when the page finishes loading. It also stays for a minimum time you set, so it never flashes on fast loads.
Q. Can I make an ASCII spinner with only CSS and no JavaScript?
Yes. The Pure CSS spinner tab gives you a one-character braille spinner made with a CSS animation, with no script and no network request. It suits buttons and inline states, stops animating for visitors who prefer reduced motion, and carries role status with a Loading label for screen readers.
Q. Which loading animation style should I use: overlay, inline or corner?
Use the full-page overlay for the first load of an app, the inline box for a panel or table that fetches its own data, and the small corner loader for background work such as saving or syncing. The builder switches between all three and updates the code, so you can preview each before you choose.
Q. How do I show a loading spinner in React while data loads?
Copy the AsciiLoader component from the React tab. It loads the script once, then renders the piece with your text, so you can use it as a Suspense fallback or render it while isLoading is true. Remove it when your data arrives and the table or page takes its place.
Q. How do I use the ASCII loader in a Vue app?
Use the AsciiLoader.vue component from the Vue tab, which loads the script when it mounts and renders the piece with your text. Vue also needs to know that ascii-art is a custom element: the last line of the file shows the isCustomElement setting to add to your Vite config.
Q. How long should a loading animation stay on screen?
Long enough not to flash: 300 to 500 milliseconds is a good minimum. The builder's Show for at least slider runs from 0 to 1,500 ms, with 400 as the default. For fast requests, wait about 300 ms before showing anything at all, so most loads finish before the loader appears.
Q. Will an ASCII loader slow down my page?
Very little. The script is a module, so it never blocks rendering, it is cached after the first visit, and each piece is a few kilobytes. Keep the loader markup in your HTML so it appears with the first paint. If you want zero network requests, use the pure CSS spinner.
Q. Is an ASCII loading spinner accessible to screen readers?
Yes. The loader has role status and aria-live polite, plus a short text such as Loading your dashboard, so screen readers announce it. The art itself is labeled with the piece name and holds a still frame for anyone who prefers reduced motion. Remove the loader when loading ends so focus never lands behind it.
Q. Which pieces work best as website loaders?
Spinner Set and Progress Loader are made for it, and Skeleton Loader shows the shape of content while it arrives. For a bigger first-load screen, Spinning Torus, Rotating Cube, Tesseract, Boot Sequence and Download Queue all loop smoothly. Any of them can also be drawn in another look, such as halftone dots or binary.
Q. Can an ASCII loader match my brand colors?
Yes. Set the background and text colors in the builder and they go straight into the generated code. To draw a colored piece in your brand's text color only, add the mono attribute to the ascii-art element. You can also pick a look such as Halftone dots or Blocks for a different texture.
Q. Can I download an ASCII spinner as a GIF?
Yes. The builder's Download GIF button records 3 seconds of the loader at 480 pixels wide, in your background and text colors. Use it where scripts cannot run, such as a README, a slide or an email. On a live website, the embed is lighter and sharper than a GIF.

Other ways to use it

Each one has its own builder.