Atelier UI®

Read the docsGithub
Docs 0.7.0

Getting started

  • Browse Catalog
  • Installation
  • How to contribute
  • Code of conduct

Components (19)

  • Dither Flow
    pro
  • Glowing Fog
    pro
  • Halftone Glow
    pro
  • Fluid Distortion
    new
  • Image Trail
    new
  • Liquid Image
    new
  • Magnetic Dot Grid
    new
  • Pixel Trail
    new
  • Pixelated Text
    new
  • Simple Scramble
    new
  • Text Bounce
    new
  • Text Fluid
    pro
  • Text Roll
    new
  • Curve Image
    new
  • Elastic Stick
    pro
  • Infinite Gallery
    new
  • Infinite Parallax
    new
  • Infinite Zoom
    new
  • Scattered Scroll
    new
Atelier UI 0.7.0 ©2026
Star on githubBuy me a coffeellms.txt
  1. Docs
  2. /
  3. Components
  4. /
  5. Simple Scramble

Simple Scramble

A simple text scramble effect that reveals characters from left to right.

JS Animation
Tailwind CSS
https://atelier-ui.com/simple-scramble

Settings

duration
1.0
See the documentation below for more options.

CLI Install

npx atelier-ui add simple-scramble

Manual Install

npm install 
simple-scramble.tsx
import { type HtmlHTMLAttributes, useCallback, useEffect, useRef } from "react"
import { useFrameLoop } from "@/registry/hooks/use-frame-loop"

export type SimpleScrambleProps = {
    duration?: number
    playOnMount?: boolean
    playOnHover?: boolean
    characters?: string
    as?: React.ElementType
} & HtmlHTMLAttributes<HTMLElement>

export function SimpleScramble({
    children,
    duration = 1,
    playOnMount = true,
    playOnHover = true,
    characters = "abcdefghijklmnopqrstuvwxyz@!#*$%^&+_[]",
    as,
    ...rest
}: SimpleScrambleProps) {
    // biome-ignore lint/suspicious/noExplicitAny: Polymorphic component
    const Tag = (as || "span") as any

    const text = typeof children === "string" ? children : ""

    const ref = useRef<HTMLElement>(null)
    const startTime = useRef(0)
    const isAnimating = useRef(false)

    const play = useCallback(() => {
        startTime.current = 0
        isAnimating.current = true
    }, [])

    useEffect(() => {
        if (playOnMount) play()
    }, [text, playOnMount, play])

    useFrameLoop((time) => {
        if (!isAnimating.current) return
        if (!ref.current) return
        if (!startTime.current) startTime.current = time

        const elapsed = time - startTime.current
        const resolved = Math.floor((elapsed / duration) * text.length)

        if (resolved >= text.length) {
            ref.current.textContent = text
            isAnimating.current = false
            return
        }

        let next = ""
        for (let i = 0; i < text.length; i++) {
            if (i < resolved) next += text[i]
            else if (text[i] === " ") next += " "
            else next += characters[Math.floor(Math.random() * characters.length)]
        }

        ref.current.textContent = next
    })

    return (
        <Tag
            ref={ref}
            onTouchStart={playOnHover ? play : undefined}
            onMouseEnter={playOnHover ? play : undefined}
            {...rest}
        >
            {text}
        </Tag>
    )
}
use-frame-loop.ts
import { useEffect, useRef } from "react"

const DELTA_MAX = 0.1

type FrameLoopCallback = (time: number, delta: number) => void

export function useFrameLoop(callback: FrameLoopCallback, interval?: number) {
    const ref = useRef(callback)
    ref.current = callback

    useEffect(() => {
        let frameId = 0
        let lastTime = 0
        let lastTick = 0

        const tick = (now: number) => {
            frameId = requestAnimationFrame(tick)

            if (interval && now - lastTick < interval) return
            if (interval) lastTick = now

            const time = now * 0.001
            const delta = lastTime ? Math.min(time - lastTime, DELTA_MAX) : 0
            lastTime = time

            ref.current(time, delta)
        }

        frameId = requestAnimationFrame(tick)

        return () => {
            cancelAnimationFrame(frameId)
        }
    }, [interval])
}

API

NameTypeDefaultDescription
durationnumber1Total duration of the reveal in seconds.
playOnMountbooleantruePlays the scramble animation on mount.
playOnHoverbooleantrueReplays the scramble animation on hover.
asReact.ElementType"span"Polymorphic tag: render as any HTML element.
charactersstring"abcdefghijkl mnopqrstuv wxyz!@#$%^&*()_+"Characters to scramble.

Credits

Yugop Nakamura
Original text scramble effect

use-scramble
A react hook for text scrambling by tolis

  • CLI Install
  • Manual Install
  • API
  • Credits
Star on githubBuy me a coffeellms.txt