Scribe Effect

Text rendering with configurable granularity (characters, words, or sentences) text animation with action queues and entrance effects.

Domma.effects.scribe()

Creates a character-by-character typing effect with support for action queues, character entrance effects, and text deletion. Perfect for hero sections, terminal interfaces, and storytelling.

// Basic usage
Domma.effects.scribe('.hero-title', {
  actions: [
    { render: 'Hello, World!' }
  ]
});

// Complex sequence with effects
const tw = Domma.effects.scribe('.element', {
  speed: 50,              // ms per character (typing)
  deleteSpeed: 30,        // ms per character (deletion)
  cursor: true,           // show blinking cursor
  cursorChar: '|',        // cursor character
  loop: true,             // loop infinitely
  actions: [
    { render: 'Hello', effect: 'bounce' },
    { wait: '2s' },
    { undoRender: true },  // delete all from last render
    { render: 'Welcome to Domma', effect: 'fade' },
    { wait: '3s' },
    { undoRender: 5 },     // delete last 5 characters
    { render: 'ma!', effect: 'glow' }
  ]
});

// Control methods
tw.pause();
tw.resume();
tw.stop();
tw.restart();
tw.destroy();

Action Types

  • { render: 'text', effect: 'none|fade|bounce|glow' } - Type text with optional entrance effect
  • { wait: '2s' } - Pause for duration (supports '2s', '500ms', or number)
  • { undoRender: true } - Delete all characters from last render
  • { undoRender: N } - Delete last N characters
  • { undoRender: 'all' } - Delete all characters

Demo: Basic Scribe

Simple typing animation with control methods.

Demo: Basic Scribe
<div class="demo-box" id="scribe-demo-1" style="font-size: 1.5rem; min-height: 3rem;"></div>
<button type="button" class="btn btn-primary" id="scribe-start-1">Start</button>
<button type="button" class="btn btn-secondary" id="scribe-pause-1">Pause</button>
<button type="button" class="btn btn-secondary" id="scribe-resume-1">Resume</button>
<button type="button" class="btn btn-danger" id="scribe-stop-1">Stop</button>
/**
 * Demo: Basic Scribe - from public/showcase/effects/scribe.
 */
export function mount(root) {
        // Store control objects
    let scribe1, scribe2, scribe3, scribe4, scribe5, scribe6;
        // scribe() Demo 1 - Basic
    $(root).find('#scribe-start-1').on('click', () => {
          if (scribe1) scribe1.destroy();
          $(root).find('#scribe-demo-1').html('');
          scribe1 = Domma.effects.scribe('#scribe-demo-1', {
            speed: 50,
            deleteSpeed: 30,
            respectMotionPreference: false,
            actions: [
              { render: 'Hello, World!' },
              { wait: '2s' },
              { undoRender: 'all' },
              { wait: '500ms' },
              { render: 'Welcome to DommaJS', effect: 'fade' }
            ]
          });
        });
    $(root).find('#scribe-pause-1').on('click', () => {
          if (scribe1) scribe1.pause();
        });
    $(root).find('#scribe-resume-1').on('click', () => {
          if (scribe1) scribe1.resume();
        });
    $(root).find('#scribe-stop-1').on('click', () => {
          if (scribe1) {
            scribe1.stop();
            scribe1 = null;
          }
        });
}
Domma.effects.scribe('#scribe-demo-1', {
  speed: 50,
  deleteSpeed: 30,
  actions: [
    { render: 'Hello, World!' },
    { wait: '2s' },
    { undoRender: 'all' },
    { wait: '500ms' },
    { render: 'Welcome to DommaJS', effect: 'fade' }
  ]
});

Demo: Character Effects

Compare different character entrance effects.

Demo: Character Effects
<div class="demo-box" id="scribe-demo-2" style="font-size: 1.5rem; min-height: 3rem;"></div>
<button type="button" class="btn btn-primary" id="scribe-effect-none">None</button>
<button type="button" class="btn btn-primary" id="scribe-effect-fade">Fade</button>
<button type="button" class="btn btn-primary" id="scribe-effect-bounce">Bounce</button>
<button type="button" class="btn btn-primary" id="scribe-effect-glow">Glow</button>
/**
 * Demo: Character Effects - from public/showcase/effects/scribe.
 */
export function mount(root) {
        // Store control objects
    let scribe1, scribe2, scribe3, scribe4, scribe5, scribe6;
        // scribe() Demo 2 - Character Effects
    function startScribeEffect(effect) {
          if (scribe2) scribe2.destroy();
          $(root).find('#scribe-demo-2').html('');
          scribe2 = Domma.effects.scribe('#scribe-demo-2', {
            speed: 40,
            respectMotionPreference: false,
            actions: [
              { render: `Effect: ${effect}`, effect: effect }
            ]
          });
        }
    $(root).find('#scribe-effect-none').on('click', () => startScribeEffect('none'));
    $(root).find('#scribe-effect-fade').on('click', () => startScribeEffect('fade'));
    $(root).find('#scribe-effect-bounce').on('click', () => startScribeEffect('bounce'));
    $(root).find('#scribe-effect-glow').on('click', () => startScribeEffect('glow'));
}
// Demonstrate each effect type
Domma.effects.scribe('#demo', {
  speed: 40,
  actions: [
    { render: `Effect: ${effect}`, effect: effect }
  ]
});

Demo: Action Sequence

Complex sequence with render, wait, and undo actions.

Demo: Action Sequence
<div class="demo-box" id="scribe-demo-3" style="font-size: 1.5rem; min-height: 3rem;"></div>
<button type="button" class="btn btn-primary" id="scribe-start-3">Start Sequence</button>
/**
 * Demo: Action Sequence - from public/showcase/effects/scribe.
 */
export function mount(root) {
        // Store control objects
    let scribe1, scribe2, scribe3, scribe4, scribe5, scribe6;
        // scribe() Demo 3 - Action Sequence
    $(root).find('#scribe-start-3').on('click', () => {
          if (scribe3) scribe3.destroy();
          $(root).find('#scribe-demo-3').html('');
          scribe3 = Domma.effects.scribe('#scribe-demo-3', {
            speed: 50,
            deleteSpeed: 30,
            respectMotionPreference: false,
            actions: [
              { render: 'Hello', effect: 'bounce' },
              { wait: '1.5s' },
              { undoRender: true },
              { render: 'Welcome to Domma', effect: 'fade' },
              { wait: '2s' }
            ]
          });
        });
}
Domma.effects.scribe('#scribe-demo-3', {
  speed: 50,
  deleteSpeed: 30,
  actions: [
    { render: 'Hello', effect: 'bounce' },
    { wait: '1.5s' },
    { undoRender: true },
    { render: 'Welcome to Domma', effect: 'fade' },
    { wait: '2s' }
  ]
});

Demo: Rendering Modes

Control rendering granularity with mode: character-by-character, word-by-word, or sentence-by-sentence.

Character Mode (default)

Character Mode (default)
<div class="demo-box" id="scribe-mode-char" style="font-size: 1.3rem; min-height: 3rem;"></div>
<button type="button" class="btn btn-primary btn-sm mb-3" id="scribe-start-mode-char">Start Character Mode</button>
/**
 * Character Mode (default) - from public/showcase/effects/scribe.
 */
export function mount(root) {
    let scribeModeChar, scribeModeWord, scribeModeSentence;
        // Mode Demos - Character Mode
    $(root).find('#scribe-start-mode-char').on('click', () => {
          if (scribeModeChar) scribeModeChar.destroy();
          $(root).find('#scribe-mode-char').html('');
          scribeModeChar = Domma.effects.scribe('#scribe-mode-char', {
            mode: 'typewriter',
            speed: 50,
            respectMotionPreference: false,
            actions: [{ render: 'Hello World!', effect: 'fade' }]
          });
        });
}
Domma.effects.scribe('#demo', {
  mode: 'typewriter',  // Character-by-character
  actions: [{ render: 'Hello World!' }]
});

Word Mode

Word Mode
<div class="demo-box" id="scribe-mode-word" style="font-size: 1.3rem; min-height: 3rem;"></div>
<button type="button" class="btn btn-primary btn-sm mb-3" id="scribe-start-mode-word">Start Word Mode</button>
/**
 * Word Mode - from public/showcase/effects/scribe.
 */
export function mount(root) {
    let scribeModeChar, scribeModeWord, scribeModeSentence;
        // Mode Demos - Word Mode
    $(root).find('#scribe-start-mode-word').on('click', () => {
          if (scribeModeWord) scribeModeWord.destroy();
          $(root).find('#scribe-mode-word').html('');
          scribeModeWord = Domma.effects.scribe('#scribe-mode-word', {
            mode: 'word',
            speed: 100,
            respectMotionPreference: false,
            actions: [{ render: 'Welcome to the Domma framework!', effect: 'bounce' }]
          });
        });
}
Domma.effects.scribe('#demo', {
  mode: 'word',  // Word-by-word
  speed: 100,    // Per word, not per character
  actions: [{ render: 'Welcome to the Domma framework!', effect: 'bounce' }]
});

Sentence Mode

Sentence Mode
<div class="demo-box" id="scribe-mode-sentence" style="font-size: 1.3rem; min-height: 4rem;"></div>
<button type="button" class="btn btn-primary btn-sm mb-3" id="scribe-start-mode-sentence">Start Sentence Mode</button>
/**
 * Sentence Mode - from public/showcase/effects/scribe.
 */
export function mount(root) {
    let scribeModeChar, scribeModeWord, scribeModeSentence;
        // Mode Demos - Sentence Mode
    $(root).find('#scribe-start-mode-sentence').on('click', () => {
          if (scribeModeSentence) scribeModeSentence.destroy();
          $(root).find('#scribe-mode-sentence').html('');
          scribeModeSentence = Domma.effects.scribe('#scribe-mode-sentence', {
            mode: 'sentence',
            speed: 200,
            respectMotionPreference: false,
            actions: [{ render: 'First sentence. Second sentence! Third sentence?', effect: 'fade' }]
          });
        });
}
Domma.effects.scribe('#demo', {
  mode: 'sentence',  // Sentence-by-sentence
  speed: 200,        // Per sentence
  actions: [{ render: 'First sentence. Second sentence! Third sentence?', effect: 'fade' }]
});

Demo: Partial Undo

Demonstrate selective unit deletion. In word mode, undoRender deletes words. In sentence mode, it deletes sentences.

Demo: Partial Undo
<div class="demo-box" id="scribe-demo-4" style="font-size: 1.5rem; min-height: 3rem;"></div>
<button type="button" class="btn btn-primary" id="scribe-start-4">Start Partial Undo</button>
/**
 * Demo: Partial Undo - from public/showcase/effects/scribe.
 */
export function mount(root) {
        // Store control objects
    let scribe1, scribe2, scribe3, scribe4, scribe5, scribe6;
        // scribe() Demo 4 - Partial Undo
    $(root).find('#scribe-start-4').on('click', () => {
          if (scribe4) scribe4.destroy();
          $(root).find('#scribe-demo-4').html('');
          scribe4 = Domma.effects.scribe('#scribe-demo-4', {
            speed: 50,
            deleteSpeed: 30,
            respectMotionPreference: false,
            actions: [
              { render: 'Domma Framework', effect: 'fade' },
              { wait: '1s' },
              { undoRender: 9 },  // Delete "Framework"
              { wait: '500ms' },
              { render: 'Effects!', effect: 'glow' },
              { wait: '2s' }
            ]
          });
        });
}
Domma.effects.scribe('#scribe-demo-4', {
  speed: 50,
  deleteSpeed: 30,
  actions: [
    { render: 'Domma Framework', effect: 'fade' },
    { wait: '1s' },
    { undoRender: 9 },  // Delete "Framework"
    { wait: '500ms' },
    { render: 'Effects!', effect: 'glow' },
    { wait: '2s' }
  ]
});

Demo: Hero Banner Loop

Infinite looping scribe for hero sections.

Demo: Hero Banner Loop
<div class="demo-box" id="scribe-demo-5" style="font-size: 2rem; font-weight: bold; min-height: 4rem; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;"></div>
<button type="button" class="btn btn-light" id="scribe-start-5">Start Loop</button>
<button type="button" class="btn btn-light" id="scribe-stop-5">Stop Loop</button>
/**
 * Demo: Hero Banner Loop - from public/showcase/effects/scribe.
 */
export function mount(root) {
        // Store control objects
    let scribe1, scribe2, scribe3, scribe4, scribe5, scribe6;
        // scribe() Demo 5 - Hero Banner Loop
    $(root).find('#scribe-start-5').on('click', () => {
          if (scribe5) scribe5.destroy();
          $(root).find('#scribe-demo-5').html('');
          scribe5 = Domma.effects.scribe('#scribe-demo-5', {
            speed: 60,
            deleteSpeed: 40,
            loop: true,
            loopDelay: 2000,
            respectMotionPreference: false,
            actions: [
              { render: 'Build Faster', effect: 'bounce' },
              { wait: '2s' },
              { undoRender: 'all' },
              { wait: '500ms' },
              { render: 'Code Smarter', effect: 'fade' },
              { wait: '2s' },
              { undoRender: 'all' },
              { wait: '500ms' },
              { render: 'Ship Better', effect: 'glow' },
              { wait: '2s' },
              { undoRender: 'all' },
              { wait: '500ms' }
            ]
          });
        });
    $(root).find('#scribe-stop-5').on('click', () => {
          if (scribe5) {
            scribe5.stop();
            scribe5 = null;
          }
        });
}
Domma.effects.scribe('#scribe-demo-5', {
  speed: 60,
  deleteSpeed: 40,
  loop: true,
  loopDelay: 2000,
  actions: [
    { render: 'Build Faster', effect: 'bounce' },
    { wait: '2s' },
    { undoRender: 'all' },
    { wait: '500ms' },
    { render: 'Code Smarter', effect: 'fade' },
    { wait: '2s' },
    { undoRender: 'all' },
    { wait: '500ms' }
  ]
});

Demo: Cursor Types

Different cursor styles with smooth fade animation.

Demo: Cursor Types
<div class="demo-box" id="scribe-demo-6" style="font-size: 1.5rem; min-height: 3rem;"></div>
<button type="button" class="btn btn-primary" id="scribe-cursor-caret">Caret |</button>
<button type="button" class="btn btn-primary" id="scribe-cursor-block">Block █</button>
<button type="button" class="btn btn-primary" id="scribe-cursor-underline">Underline _</button>
<button type="button" class="btn btn-secondary" id="scribe-cursor-noblink">No Blink</button>
/**
 * Demo: Cursor Types - from public/showcase/effects/scribe.
 */
export function mount(root) {
        // Store control objects
    let scribe1, scribe2, scribe3, scribe4, scribe5, scribe6;
        // scribe() Demo 6 - Cursor Types
    function startCursorDemo(cursorType, cursorChar, cursorBlink = true) {
          if (scribe6) scribe6.destroy();
          $(root).find('#scribe-demo-6').html('');
          scribe6 = Domma.effects.scribe('#scribe-demo-6', {
            speed: 50,
            cursorType: cursorType,
            cursorChar: cursorChar,
            cursorBlink: cursorBlink,
            respectMotionPreference: false,
            actions: [
              { render: 'Cursor type: ' + cursorType }
            ]
          });
        }
    $(root).find('#scribe-cursor-caret').on('click', () => startCursorDemo('caret', '|'));
    $(root).find('#scribe-cursor-block').on('click', () => startCursorDemo('block', '█'));
    $(root).find('#scribe-cursor-underline').on('click', () => startCursorDemo('underline', '_'));
    $(root).find('#scribe-cursor-noblink').on('click', () => startCursorDemo('caret', '|', false));
}
// Different cursor styles
Domma.effects.scribe('#demo', {
  speed: 50,
  cursorType: 'caret',  // or 'block', 'underline'
  cursorChar: '|',       // or '█', '_'
  cursorBlink: true,     // or false
  actions: [
    { render: 'Cursor type: caret' }
  ]
});