> ## Documentation Index
> Fetch the complete documentation index at: /llms.txt
> Use this file to discover all available pages before exploring further.

# Notes Typing

> The notes-app confession as a mountable scene: a bold title, then body lines typed character by character on the measured human keystroke rhythm, the caret riding every landed character and jumping down at each paragraph. For a bare prompt line use typed-prompt; for an even mechanical reveal use typewriter

# Notes Typing

The notes-app confession as a mountable scene: a bold title, then body lines typed character by character on the measured human keystroke rhythm, the caret riding every landed character and jumping down at each paragraph. For a bare prompt line use typed-prompt; for an even mechanical reveal use typewriter

**Notes Typing**

```html notes-typing.html theme={null}
<!doctype html>
<!--
  notes-typing: HyperFrames motion primitive (scene)

  The notes-app confession, as a mountable scene: a bold title, then body
  lines typed character by character on the measured human keystroke rhythm
  - uneven, bursty, never a metronome - with the caret riding the right edge
  of every landed character and jumping down at each paragraph.

  This is the notes-app SCENE. For a bare prompt line typing itself, use
  typed-prompt; for an even mechanical reveal, use typewriter.

  #root only, container-type: size, one paused timeline registered under the
  literal "notes-typing" key. Deterministic: the schedule is generated from a
  measured gap table; caret positions are measured at mount.
-->
<html
  lang="en"
  data-composition-id="notes-typing"
  data-composition-duration="10"
  data-composition-variables='[{"id": "title", "type": "string", "role": "content", "label": "Title", "description": "Bold note title. A | breaks the line.", "default": "Things nobody told me|about video."}, {"id": "lines", "type": "string", "role": "content", "label": "Lines", "description": "Pipe-separated body lines, typed one after another.", "default": "my videos sucked|my tools cost $$$|and re-edits never end."}, {"id": "speed", "type": "number", "role": "timing", "label": "Speed", "description": "Typing speed multiplier. 1 is the measured human rhythm.", "default": 1, "min": 0.5, "max": 2, "step": 0.1}]'
>
  <head>
    <meta charset="UTF-8" />
    <title>Notes Typing</title>
  </head>
  <body>
    <template>
      <div id="root" data-composition-id="notes-typing" data-duration="10" data-fps="30">
        <style>
          *,
          *::before,
          *::after {
            box-sizing: border-box;
          }
          #root {
            position: absolute;
            inset: 0;
            overflow: hidden;
            container-type: size;
            isolation: isolate;
            background: #fefefe;
            font-family: var(
              --font-body,
              -apple-system,
              BlinkMacSystemFont,
              "Segoe UI",
              sans-serif
            );
            pointer-events: none;
          }
          .nty-nav {
            position: absolute;
            left: 4cqw;
            top: 3.5cqh;
            width: 10cqmin;
            height: 10cqmin;
            border-radius: 50%;
            background: #fff;
            box-shadow: 0 0.4cqmin 1.4cqmin rgba(0, 0, 0, 0.07);
          }
          .nty-nav svg {
            position: absolute;
            left: 32%;
            top: 30%;
            width: 36%;
            height: 40%;
          }
          .nty-check {
            position: absolute;
            right: 4cqw;
            top: 3.5cqh;
            width: 10cqmin;
            height: 10cqmin;
            border-radius: 50%;
            background: #fec909;
          }
          .nty-check svg {
            position: absolute;
            left: 26%;
            top: 30%;
            width: 50%;
            height: 42%;
          }
          .nty-body {
            position: absolute;
            left: 6cqw;
            right: 6cqw;
            top: 16cqh;
          }
          .nty-title {
            font-size: 8.4cqmin;
            font-weight: 700;
            letter-spacing: -0.005em;
            color: #191919;
            line-height: 1.15;
            margin: 0 0 3.4cqh;
          }
          .nty-line {
            font-size: 4.9cqmin;
            color: #191919;
            line-height: 1.5;
            margin: 0 0 2.6cqh;
            white-space: pre-wrap;
            min-height: 1.5em;
          }
          .nty-ch {
            opacity: 0;
          }
          #nty-caret {
            position: absolute;
            width: 0.42cqmin;
            height: 5.2cqmin;
            background: #ffca00;
            border-radius: 0.2cqmin;
            left: 0;
            top: 0;
            will-change: transform;
          }
          .nty-fmt {
            position: absolute;
            left: 4cqw;
            right: 4cqw;
            bottom: 4cqh;
            height: 8.5cqh;
            background: #fdfdfd;
            border-radius: 100cqmin;
            box-shadow: 0 0.4cqmin 1.6cqmin rgba(0, 0, 0, 0.05);
            display: flex;
            align-items: center;
            justify-content: space-around;
          }
          .nty-fmt .aa {
            font-size: 4.6cqmin;
            font-weight: 700;
            color: #111;
          }
          .nty-fmt .aa small {
            font-size: 0.65em;
            font-weight: 400;
          }
          .nty-fmt svg {
            width: 5.6cqmin;
            height: 5.6cqmin;
          }
        </style>
        <div class="nty-nav">
          <svg viewBox="0 0 40 40">
            <path
              d="M26 4 L10 20 L26 36"
              fill="none"
              stroke="#111"
              stroke-width="4.5"
              stroke-linecap="round"
              stroke-linejoin="round"
            />
          </svg>
        </div>
        <div class="nty-check">
          <svg viewBox="0 0 56 46">
            <path
              d="M6 24 L21 38 L50 6"
              fill="none"
              stroke="#fff"
              stroke-width="7"
              stroke-linecap="round"
              stroke-linejoin="round"
            />
          </svg>
        </div>
        <div class="nty-body" id="nty-body">
          <div class="nty-title" id="nty-title" data-layout-allow-overlap>
            Things nobody told me<br />about video.
          </div>
          <div id="nty-lines"></div>
          <div id="nty-caret"></div>
        </div>
        <div class="nty-fmt">
          <span class="aa">A<small>a</small></span>
          <svg viewBox="0 0 64 54">
            <circle cx="10" cy="12" r="9" fill="none" stroke="#111" stroke-width="4" />
            <path
              d="M6 12 l3 3 6 -7"
              fill="none"
              stroke="#111"
              stroke-width="3"
              stroke-linecap="round"
            />
            <line
              x1="28"
              y1="12"
              x2="60"
              y2="12"
              stroke="#111"
              stroke-width="4"
              stroke-linecap="round"
            />
            <circle cx="10" cy="42" r="9" fill="none" stroke="#111" stroke-width="4" />
            <line
              x1="28"
              y1="42"
              x2="52"
              y2="42"
              stroke="#111"
              stroke-width="4"
              stroke-linecap="round"
            />
          </svg>
          <svg viewBox="0 0 62 62">
            <rect
              x="3"
              y="3"
              width="56"
              height="56"
              rx="8"
              fill="none"
              stroke="#111"
              stroke-width="4"
            />
            <line x1="3" y1="22" x2="59" y2="22" stroke="#111" stroke-width="4" />
            <line x1="22" y1="3" x2="22" y2="59" stroke="#111" stroke-width="4" />
          </svg>
          <svg viewBox="0 0 46 70">
            <path
              d="M33 14 v34 a11 11 0 0 1 -22 0 V16 a8 8 0 0 1 16 0 v30 a4 4 0 0 1 -8 0 V20"
              fill="none"
              stroke="#111"
              stroke-width="4"
              stroke-linecap="round"
            />
          </svg>
          <svg viewBox="0 0 62 62">
            <circle cx="31" cy="31" r="27" fill="none" stroke="#111" stroke-width="4" />
            <path d="M22 40 L36 18 a5 5 0 0 1 8 6 L30 46 l-10 4 z" fill="#111" />
          </svg>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
        <script>
          (function () {
            "use strict";
            var compositionId = "notes-typing";
            var vars =
              window.__hyperframes && window.__hyperframes.getVariables
                ? window.__hyperframes.getVariables()
                : {};
            function text(v, fb, mx) {
              if (typeof v !== "string") return fb;
              var t = v.trim();
              if (!t) return fb;
              return t.length > mx ? t.slice(0, mx) : t;
            }
            function clampNumber(v, min, max, fb) {
              var n = typeof v === "number" ? v : parseFloat(v);
              if (!isFinite(n)) return fb;
              return Math.min(max, Math.max(min, n));
            }
            var titleEl = document.getElementById("nty-title");
            titleEl.innerHTML = "";
            text(vars.title, "Things nobody told me|about video.", 60)
              .split("|")
              .forEach(function (line, i) {
                if (i > 0) titleEl.appendChild(document.createElement("br"));
                titleEl.appendChild(document.createTextNode(line));
              });
            var lines = text(
              vars.lines,
              "my videos sucked|my tools cost $$$|and re-edits never end.",
              240,
            )
              .split("|")
              .map(function (l) {
                return l.trim();
              })
              .filter(Boolean)
              .slice(0, 6);
            if (lines.length === 0) lines = ["my videos sucked"];
            var speed = clampNumber(vars.speed, 0.5, 2, 1);

            var linesEl = document.getElementById("nty-lines");
            var chars = [];
            lines.forEach(function (line, li) {
              var p = document.createElement("div");
              p.className = "nty-line";
              p.setAttribute("data-layout-allow-overlap", "");
              p.setAttribute("data-layout-allow-occlusion", "");
              line.split("").forEach(function (ch) {
                var s = document.createElement("span");
                s.className = "nty-ch";
                s.textContent = ch;
                p.appendChild(s);
                chars.push({ el: s, line: li });
              });
              linesEl.appendChild(p);
            });

            /* the measured human keystroke rhythm, cycled */
            var KEY_GAPS = [
              0.1, 0.033, 0.2, 0.133, 0.2, 0.033, 0.067, 0.033, 0.1, 0.067, 0.033, 0.067, 0.067,
              0.067, 0.133, 0.067, 0.033, 0.067, 0.1, 0.1, 0.067, 0.133, 0.1, 0.033, 0.1, 0.033,
              0.167, 0.033, 0.1, 0.1,
            ];
            var LINE_PAUSE = 0.85; /* the measured breath before a new thought */

            var caret = document.getElementById("nty-caret");
            var body = document.getElementById("nty-body");
            var tl = gsap.timeline({ paused: true });
            function build() {
              tl.clear();
              var bodyRect = body.getBoundingClientRect();
              var scale = bodyRect.width / body.offsetWidth || 1;
              gsap.set(caret, { autoAlpha: 0 });
              var t = 0.5;
              var lastLine = -1;
              chars.forEach(function (c, i) {
                if (c.line !== lastLine) {
                  if (lastLine >= 0) t += LINE_PAUSE / speed;
                  lastLine = c.line;
                }
                var r = c.el.getBoundingClientRect();
                var x = (r.right - bodyRect.left) / scale;
                var y = (r.top - bodyRect.top) / scale;
                tl.set(c.el, { opacity: 1 }, t);
                tl.set(caret, { x: x + 2, y: y, autoAlpha: 1 }, t);
                t += KEY_GAPS[i % KEY_GAPS.length] / speed;
              });
              /* settle blink, then rest */
              for (var b = 0; b < 4; b += 1) {
                tl.set(caret, { autoAlpha: b % 2 === 0 ? 0 : 1 }, t + 0.4 + b * 0.5333);
              }
              tl.set({}, {}, 10);
              tl.seek(0);
            }
            build();
            if (document.fonts && document.fonts.ready) {
              document.fonts.ready.then(function () {
                build();
                tl.pause(0);
              });
            }
            window.__timelines = window.__timelines || {};
            window.__timelines[compositionId] = tl;
          })();
        </script>
      </div>
    </template>
  </body>
</html>
```

Copy npx hyperframes add notes-typing to the clipboard

That writes one file: `compositions/components/notes-typing.html`.

## Paste it into your composition

Open `compositions/components/notes-typing.html` and copy what is inside into your own composition.

A component has no size or duration of its own. It takes both from the composition
you paste it into.

## Variables

Every one of these has a default, so the piece works untouched. Set the ones you
want to change on the element:

| Variable | Default | Accepts | What it does | | |
| - | - | - | - | - | - |
| `title` | \`Things nobody told me | about video.\` | string | Bold note title. A | breaks the line. |
| `lines` | \`my videos sucked | my tools cost \$\$\$ | and re-edits never end.\` | string | Pipe-separated body lines, typed one after another. |
| `speed` | `1` | 0.5 to 2, step 0.1 | Typing speed multiplier. 1 is the measured human rhythm. | | |

Set them with `data-variable-values` on the element that mounts it. These are the
defaults, so this behaves exactly like the preview above until you change one:

```html wrap theme={null}
<div
  data-composition-id="notes-typing"
  data-composition-src="compositions/components/notes-typing.html"
  data-variable-values='{"title":"Things nobody told me|about video.","lines":"my videos sucked|my tools cost $$$|and re-edits never end.","speed":1}'
></div>
```

Tagged `motion-primitive` `notes` `typing` `mock-ui` `vertical`.

Created by Miao Yang.

## Related topics

* [Browse the complete Catalog](/catalog.md)
* [Add assets and Catalog items in the browser editor](/studio/assets-and-blocks.md)
* [Put blocks together into one video](/concepts/compositions.md)


## Related topics

- [Notes Reveal](/catalog/blocks/notes-reveal.md)
- [Flowchart](/catalog/blocks/flowchart.md)
- [Flowchart Vertical](/catalog/blocks/flowchart-vertical.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.