Lib

Components/Cursor Effects/Liquid Ascii

Liquid Ascii

FLIP fluid simulation rendered as ASCII characters. An ascii liquid effect for React: real fluid dynamics rendered as text, free and open source with zero dependencies.

liquid-asciiv0.1.0MIT
Liquid ASCII fluid simulation. Enable JavaScript and move your cursor through it.

drag through the fluid

Customize

Auto wave (idle motion)

Code

bash
npx shadcn@latest add https://lib.tirup.in/r/liquid-ascii.json
tsx
import { LiquidAsciiDemo } from "@/components/liquid-ascii"

export function Hero() {
  return (
    <div className="h-[440px]">
      <LiquidAsciiDemo
        speed={1}
        cellSize={15}
        gravity={-25}
        fillHeight={0.5}
        cursorRadius={0.25}
        cursorForce={66}
        autoWave={true}
      />
    </div>
  )
}

The snippet above follows your playground settings live. Installed from lib.tirup.in, one file, zero dependencies. Getting started guide.

Props

PropTypeDefaultDescription
speednumber1Simulation timestep multiplier (0.1–3).
cellSizenumber15Character cell size in pixels (6–30). Drives sim + render together.
gravitynumber-25Gravity strength, negative = downward (−50–0).
fillHeightnumber0.5Fill fraction of the tank (0–1).
cursorRadiusnumber0.25Mouse influence as a fraction of the short side (0–0.5).
cursorForcenumber66Strength of the cursor push force (0–200).
autoWavebooleantrueGentle idle waves when untouched. Motion is input-driven otherwise.
classNamestring""Additional classes for the canvas element.

How I built it

No video loops, no CSS blobs. This is a genuine fluid solver: the same PIC/FLIP family behind film water effects, compressed until it runs at 60fps inside a grid of text characters. One file, zero libraries, and every number below matches the code you just installed.

Fall

Every frame takes real delta time (clamped to 1/240–1/45s, scaled by speed) and runs two half-steps instead of one big jump. That's what keeps fast splashes stable. Gravity integrates first: velocity grows by (|gravity| / 25) × 1500 × dt each step.

Splat

Up to 20,000 particles (4 per cell) each cast two votes: horizontal velocity goes onto the vertical grid faces, vertical velocity onto the horizontal faces (a staggered MAC layout), normalized per face. Faces touching solid cells are wall faces, prescribed zero. They are the only zeros in the system. The result is snapshotted, and that snapshot is the memory the FLIP blend needs later.

Squeeze

Cells holding particles are marked fluid; the border is solid wall. Then 30 Jacobi iterations with 1.5 over-relaxation push the pressure field until every fluid cell agrees with its neighbours: divergence is read straight off the faces in a compact one-cell stencil, with the zeroed wall faces feeding the true no-penetration signal, and the gradient written back is its exact adjoint, so pressure can neither pump energy in nor leak volume out.

Return

Motion flows back to the particles as a 28/72 blend: a little of the smooth face velocity (stable, mushy) and a lot of the FLIP delta versus the snapshot (lively, detailed), sampled from interior faces only so the strict-zero wall faces steady the solve without ever damping a particle. Speeds clamp at 1500, particles advect, walls stop only the head-on component while sideways slide survives with light friction, with a 0.35-cell gap so liquid reaches the edges. Overfull cells get hash-jittered apart and up to 200 lost particles reseed per frame.

Touch

Your cursor is a spoon, not a magnet. Pointer velocity is smoothed and a 3×3 neighbourhood is checked first. Moving through empty air does nothing; only contact pushes, with strength falling off quadratically inside your cursor radius. Leave it alone for 2 seconds and a slow wandering column nudges the surface so the showcase stays breathing.

Ink

Each column's surface is the first row with real particle presence. Surface cells get the heaviest glyphs, fast spray above the surface gets mid-weight marks, and depth below fades through noise-weighted characters, with deliberately low cutoffs so hard stirring spreads the liquid instead of thinning it into nothing. Colors are pre-built per alpha step (zero string garbage per frame) and the theme follows your dark class live.

Honest limits

One tank, two dimensions, no obstacles, and cellSize is the whole performance budget (6–30px drives sim, particles, and glyphs together). The loop sleeps when settled, pauses offscreen and in hidden tabs, draws one still frame for reduced-motion users, caps pixel ratio at 2, and debounces rebuilds at 120ms. Resizing or changing cell size holds exactly 4 particles per cell, adding or randomly thinning as needed, so the quantity of liquid never changes in any box, at any cell size.

Inspiration credit

The concept is inspired by React Bits' Liquid ASCII. My solver, renderer, and interaction code above are written from scratch. Go compare them side by side.

Questions

What is Liquid Ascii?

Liquid Ascii is a free, open-source React component that renders a real-time FLIP fluid simulation as ASCII text on a canvas. Move your cursor through it and the liquid splashes in characters. It has zero dependencies and ships as a single file you own forever.

How do I install Liquid Ascii?

Run npx shadcn@latest add https://lib.tirup.in/r/liquid-ascii.json in your Next.js or React project, then render the component inside any sized container. It adapts to light and dark mode automatically.

Is Liquid Ascii free for commercial use?

Yes. It is MIT licensed, free forever. Copy it, modify it, ship it in commercial projects. No keys, no lock-in, no paid core.

Does Liquid Ascii work on mobile?

It is desktop-first: the cursor interaction needs a precise pointer. Gate mounting behind a pointer or screen-size check and skip it on small screens. It respects prefers-reduced-motion with a still frame.

How does the ASCII liquid effect actually work?

Up to 20,000 particles carry the fluid on a staggered MAC grid. Each frame runs gravity, a bilinear splat to grid faces, a 30-iteration pressure solve that keeps volume, and a PIC/FLIP blend back to particles. Density is binned per cell and rendered as glyphs: heavy characters on the surface, spray above it, depth-faded characters below.