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

# Slot Machine Roll

> A vertical reel scrolls characters from one value to another.

export const InstallCommand = ({command}) => {
  const [copied, setCopied] = React.useState(false);
  const copy = async () => {
    try {
      if (navigator.clipboard && window.isSecureContext) {
        await navigator.clipboard.writeText(command);
      } else {
        const previous = document.activeElement;
        const scratch = document.createElement("textarea");
        scratch.value = command;
        scratch.setAttribute("readonly", "");
        scratch.style.position = "fixed";
        scratch.style.opacity = "0";
        document.body.appendChild(scratch);
        scratch.select();
        document.execCommand("copy");
        document.body.removeChild(scratch);
        previous?.focus?.();
      }
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    } catch {}
  };
  return <div className="hf-install-command not-prose my-4 flex items-stretch overflow-hidden rounded-xl border border-zinc-200 bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-900">
      <code className="flex-1 overflow-x-auto whitespace-nowrap border-r border-zinc-200 px-4 py-3 font-mono text-sm text-zinc-800 dark:border-zinc-800 dark:text-zinc-100">
        {command}
      </code>
      <button type="button" onClick={copy} data-copied={copied ? "true" : "false"} aria-label={`Copy ${command} to the clipboard`} className="hf-install-copy">
        <svg className="hf-install-copy-clipboard" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
          <path d="M14.25 5.25H7.25C6.14543 5.25 5.25 6.14543 5.25 7.25V14.25C5.25 15.3546 6.14543 16.25 7.25 16.25H14.25C15.3546 16.25 16.25 15.3546 16.25 14.25V7.25C16.25 6.14543 15.3546 5.25 14.25 5.25Z" />
          <path d="M2.80103 11.998L1.77203 5.07397C1.61003 3.98097 2.36403 2.96397 3.45603 2.80197L10.38 1.77297C11.313 1.63397 12.19 2.16297 12.528 3.00097" />
        </svg>
        <svg className="hf-install-copy-check" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
          <path d="M2.75 9.5L6.5 13.25L15.25 4.5" />
        </svg>
      </button>
      <span className="hf-install-copy-status" role="status" aria-live="polite">
        {copied ? "Copied" : ""}
      </span>
    </div>;
};

<iframe className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800" title="slot-machine-roll preview" loading="lazy" srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@0.7/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/components/slot-machine-roll.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/slot-machine-roll.png");document.body.appendChild(p)});<\/script></body></html>`} />

## Install

<InstallCommand command="npx hyperframes add slot-machine-roll" />

That writes one file: `compositions/components/slot-machine-roll.html`.

## 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                                                                                                         |
| ----------- | ---------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `result`    | `A7K`      | string                       | Characters the reels land on, one reel column per character. Whitespace is stripped and every reel starts on a zero. |
| `direction` | `up`       | `up`, `down`                 | Which way the reels roll: up lifts the zero out through the top, down drops it out through the bottom.               |
| `size`      | `standard` | `small`, `standard`, `large` | Reel height and type size. The roll distance scales with it so the reel always lands exactly one row on.             |
| `tone`      | `ink`      | `ink`, `paper`, `accent`     | Character colour: ink for light frames, paper for dark ones, accent rides --brand.                                   |

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="slot-machine-roll"
  data-composition-src="compositions/components/slot-machine-roll.html"
  data-variable-values='{"result":"A7K","direction":"up","size":"standard","tone":"ink"}'
></div>
```

## Source

<Accordion title={`slot-machine-roll.html`}>
  ```html theme={null}
  <!--
    Slot Machine Roll - remocn typography primitive for HyperFrames.

    Paste the markup, CSS and script into a composition. Drive the timeline
    integration from a paused GSAP timeline so renders remain deterministic.

    Variables. The script reads each one, falls back to the declared default on
    anything missing or unrecognised, and writes the result as a custom property
    the CSS below consumes. The timeline recipe stays untouched by them: it keeps
    driving one vertical offset of exactly one row, and these decide how tall that
    row is, which way it travels and what it lands on.

      - result (text, default "A7K"): the characters the reels land on, one reel
        column per character. Whitespace is stripped, and blank or non-text input
        falls back to the default three. Every reel starts on a zero.
      - direction (up | down, default up): which way the reels roll. up lifts the
        zero out through the top, down drops it out through the bottom.
      - size (small | standard | large, default standard): reel height and type
        size, 48px, 74px or 104px. The roll distance is scaled by the same factor,
        so whatever the size the reel still lands exactly one row on.
      - tone (ink | paper | accent, default ink): character colour. ink is
        near-black for light frames, paper near-white for dark ones, accent rides
        --brand.

    On every default the result is identical to the original: three 74px reels in
    near-black rolling 0 up to A, 7 and K.
  -->

  <div
    class="hf-remocn-slot-machine-roll"
    data-composition-variables='[
      { "id": "result", "type": "string", "role": "content", "label": "Result", "description": "Characters the reels land on, one reel column per character. Whitespace is stripped and every reel starts on a zero.", "default": "A7K" },
      { "id": "direction", "type": "enum", "role": "motion", "label": "Direction", "description": "Which way the reels roll: up lifts the zero out through the top, down drops it out through the bottom.", "default": "up", "options": [{ "value": "up", "label": "Up" }, { "value": "down", "label": "Down" }] },
      { "id": "size", "type": "enum", "role": "layout", "label": "Size", "description": "Reel height and type size. The roll distance scales with it so the reel always lands exactly one row on.", "default": "standard", "options": [{ "value": "small", "label": "Small" }, { "value": "standard", "label": "Standard" }, { "value": "large", "label": "Large" }] },
      { "id": "tone", "type": "enum", "role": "style", "label": "Tone", "description": "Character colour: ink for light frames, paper for dark ones, accent rides --brand.", "default": "ink", "options": [{ "value": "ink", "label": "Ink" }, { "value": "paper", "label": "Paper" }, { "value": "accent", "label": "Accent" }] }
    ]'
  >
    <span
      ><i>0<br />A</i></span
    ><span
      ><i>0<br />7</i></span
    ><span
      ><i>0<br />K</i></span
    >
  </div>

  <style>
    .hf-remocn-slot-machine-roll {
      color: var(--hf-slot-color, #18181b);
      font-family: Inter, system-ui, sans-serif;
      font-weight: 900;
      letter-spacing: -0.04em;
    }
    .hf-remocn-slot-machine-roll {
      display: inline-flex;
      gap: 6px;
      height: var(--hf-slot-row, 74px);
      overflow: hidden;
      font:
        900 var(--hf-slot-row, 74px)/1 ui-monospace,
        SFMono-Regular,
        Menlo,
        monospace;
    }
    .hf-remocn-slot-machine-roll span {
      display: block;
      height: var(--hf-slot-row, 74px);
      overflow: hidden;
    }
    .hf-remocn-slot-machine-roll i {
      display: block;
      font-style: normal;
      transform: translateY(
        calc(
          var(--hf-slot-base, 0px) + var(--hf-slot-y, 0px) * var(--hf-slot-dir, 1) *
            var(--hf-slot-gain, 1)
        )
      );
    }
  </style>

  <script>
    (function () {
      "use strict";

      var vars =
        window.__hyperframes && window.__hyperframes.getVariables
          ? window.__hyperframes.getVariables()
          : {};

      // Unrecognised overrides return to their declared defaults.
      function pick(table, value, fallback) {
        return Object.prototype.hasOwnProperty.call(table, value) ? value : fallback;
      }

      var directions = { up: 1, down: -1 };
      var sizes = { small: 48, standard: 74, large: 104 };
      var tones = {
        ink: "#18181b",
        paper: "#fafafa",
        accent: "var(--brand, #34d399)",
      };

      var dir = directions[pick(directions, vars.direction, "up")];
      var row = sizes[pick(sizes, vars.size, "standard")];
      var tone = tones[pick(tones, vars.tone, "ink")];
      var result = (typeof vars.result === "string" ? vars.result : "").replace(/\s+/g, "");
      if (result.length === 0) result = "A7K";

      var roots = document.querySelectorAll(".hf-remocn-slot-machine-roll");
      for (var i = 0; i < roots.length; i += 1) {
        var root = roots[i];
        root.style.setProperty("--hf-slot-row", row + "px");
        root.style.setProperty("--hf-slot-gain", String(row / 74));
        root.style.setProperty("--hf-slot-dir", String(dir));
        // Rolling down starts one row up, so the zero still sits in frame at rest.
        root.style.setProperty("--hf-slot-base", (dir === 1 ? 0 : -row) + "px");
        root.style.setProperty("--hf-slot-color", tone);
        root.textContent = "";
        for (var j = 0; j < result.length; j += 1) {
          var reel = document.createElement("i");
          reel.appendChild(document.createTextNode(dir === 1 ? "0" : result.charAt(j)));
          reel.appendChild(document.createElement("br"));
          reel.appendChild(document.createTextNode(dir === 1 ? result.charAt(j) : "0"));
          var slot = document.createElement("span");
          slot.appendChild(reel);
          root.appendChild(slot);
        }
      }
    })();
  </script>

  <!--
    Timeline integration:
    tl.fromTo('.hf-remocn-slot-machine-roll i', { '--hf-slot-y': '0px' }, { '--hf-slot-y': '-74px', duration: 0.52, stagger: 0.06, ease: 'power3.inOut' }, startTime);
  -->
  ```
</Accordion>

## Usage

Open `compositions/components/slot-machine-roll.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.

Tagged `video-primitive` `motion-primitive` `remocn-port` `typography`.

## Related topics

* [Browse the complete Catalog](/catalog)
* [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
* [Build a richer composition](/go-further)


## Related topics

- [Canary rollouts](/contributing/canary-rollouts.md)
- [Kinetic Type Swap](/catalog/components/kinetic-type-swap.md)
- [Ticker Takeover](/catalog/components/ticker-takeover.md)
