npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@turnwise/cube-scramble

v1.0.0

Published

WCA-style random-state scrambles for the 3×3 Rubik's Cube.

Readme

@turnwise/cube-scramble

WCA-style scrambles for the 3×3 Rubik's Cube, written in TypeScript, for browsers, workers, Node, Deno and Bun.

Each scramble leads to a random position, and every position that needs at least two moves to solve is equally likely, as WCA Regulation 4b3 asks. Blindfolded scrambles also leave the cube in a random orientation, as Regulation 4b3a asks. The randomness comes from the platform's cryptographically secure generator.

pnpm add @turnwise/cube-scramble

See it working

Every scramble in Turnwise comes from this package. Open the menu and choose Scramble for a fresh random scramble, or watch the cube on the home page scramble itself.

Get a scramble

import { formatScramble, scramble } from '@turnwise/cube-scramble';

const { moves, faces } = scramble();

formatScramble(moves);
// for example "R2 U2 L2 D L2 U F2 D L' U2 L2 D2 U R2 D L F L U'"

faces.U;
// the nine stickers on top afterwards; for the example above, ['D', 'U', 'U', 'B', 'U', 'B', 'U', 'D', 'L']

Apply the moves to a solved cube held white on top and green in front, as WCA Regulation 4d1 describes. In the package's terms that is the U face up and the F face towards you: the moves name faces, not colours.

moves is an array of Move values, the plain strings 'R2', 'U_PRIME' and so on, so it survives JSON, postMessage and a database unchanged. formatScramble turns it into the traditional notation people read and other cube programs accept.

faces is the cube the moves leave: all 54 stickers, face by face, each named by the face it belonged to on the solved cube. On a standard cube 'U' is white and 'F' is green. Compare it with a scan to check that a scramble was applied correctly.

Blindfolded scrambles

Blindfolded events have no inspection, so nobody gets to turn the cube to a favourite side before the clock starts. The WCA makes the orientation part of the scramble instead. blindfoldedScramble draws a scramble the same way, then adds up to two wide moves that turn the cube to one of its 24 orientations, each equally likely.

import { blindfoldedScramble, formatScramble } from '@turnwise/cube-scramble';

const { moves, faces, upright } = blindfoldedScramble();

formatScramble(moves);
// for example "L2 F2 D F2 L2 D L2 B2 D L2 D R' D' B L2 U L R' D2 B F' Rw Uw'"

faces.U[4]; // 'F': the cube ends with the green centre on top
faces.F[4]; // 'L': and the orange centre in front

upright.U;
// the same cube turned white on top and green in front: ['U', 'R', 'R', 'R', 'U', 'F', 'L', 'D', 'B']

Do the whole scramble in one go on the same cube, held white on top and green in front, including the wide moves at the end. A wide move turns a face together with the middle layer next to it:

  • Rw turns the right face and the middle layer as one block, the way R turns: the front of that block goes up. The left layer stays still.
  • Fw turns the front face and the middle layer behind it, the way F turns: the top of that block goes to the right. The back layer stays still.
  • Uw turns the top face and the middle layer below it, the way U turns: the front of that block goes to the left. The bottom layer stays still.

2 turns twice, and ' turns the other way. Afterwards the centres show the new orientation, as faces does. One scramble in 24 has no wide moves and leaves the cube white on top and green in front. For Multi-Blind, call it once for each cube.

To check a blindfolded scramble with a scanner, compare the scan with upright, not faces. A scanner cannot see how the cube is held, so it files every scan white on top and green in front, whichever way the wide moves turned the cube. upright is the scrambled cube filed the same way.

What you get

| Effort | scramble | blindfoldedScramble, wide moves included | | --- | --- | --- | | Efforts.full, the default | usually 18 to 22 moves; 16 and 17 are rare; never more than 22 in our measurements | 19 to 25 moves in our measurements | | Efforts.fast | usually 18 to 24 moves, in a fraction of the time; as few as 15 in our measurements | 18 to 27 moves in our measurements |

A scramble is never shorter than two moves, and never lands on a position that is solved or one move from solved.

import { Efforts, scramble } from '@turnwise/cube-scramble';

scramble({ effort: Efforts.fast });

Leave an option out to get its default. There is no other way to ask for a default.

Reproducible scrambles

Pass a random function to control where the randomness comes from. It must return numbers from 0 up to, but not including, 1, as Math.random does. The same function, started from the same seed, gives the same scramble on every machine. The same goes for blindfoldedScramble, orientation included.

import { formatScramble, scramble } from '@turnwise/cube-scramble';

// Any seeded generator works; this one is mulberry32.
function seeded(seed: number): () => number {
  let state = seed >>> 0;
  return () => {
    state = (state + 0x6d2b79f5) >>> 0;
    let mixed = state;
    mixed = Math.imul(mixed ^ (mixed >>> 15), mixed | 1);
    mixed ^= mixed + Math.imul(mixed ^ (mixed >>> 7), mixed | 61);
    return ((mixed ^ (mixed >>> 14)) >>> 0) / 4294967296;
  };
}

formatScramble(scramble({ random: seeded(42) }).moves);
// "F2 U F2 U' F2 R2 D L2 B R D2 L U2 L2 B2 D' F' U L2 U", every time, on every machine

Use this for tests, replays and shared practice sets, not for fairness. A generator started from a 32-bit seed can reach at most about four billion scrambles, and a 3×3 cube has about 43 quintillion positions. Leave random out when every position must be possible.

Speed, and where to run it

| | Laptop | iPhone | | --- | --- | --- | | prepare(), or the first scramble without it | about 0.3 s | about 0.5 s | | scramble() | about 0.1 s | about 0.1 s | | scramble({ effort: Efforts.fast }) | a few milliseconds | under 10 ms |

blindfoldedScramble() usually takes as long as scramble(). About one in three takes longer, while it finds a last face turn that does not share an axis with the first wide move: one in ten takes about twice as long, and the slowest we measured took about five times as long.

Both are synchronous and keep their thread busy. In a browser, run them in a worker, and call prepare() there first so the first scramble does not pay for building its lookup tables. The documentation has a complete worker.

When something is wrong

| Error | When | | --- | --- | | RangeError | Your random function returned something outside 0 up to, but not including, 1. | | TypeError | effort is not a known effort, or formatScramble was given something that is not a move. The compiler rejects both before they can happen, unless the call comes from plain JavaScript or unchecked data. |

Documentation

docs/documentation.md is the full reference: what a scramble guarantees, every export with its signature and an example, the exact error messages, and how to run it in a worker.

It ships inside the package, at node_modules/@turnwise/cube-scramble/docs/documentation.md, so it always matches the version you have installed. Its examples are compiled against the published types on every change.

| Task | Exports | | --- | --- | | Scramble | scramble, blindfoldedScramble, ScrambleOptions, Scramble, BlindfoldedScramble | | Moves | formatScramble, Move, WideMove | | The cube | FaceGrid, FaceStickers, Face | | Setup | prepare, Efforts, Effort |

The package is ESM only. Node 22.12 and later can also load it with require.

License

Apache-2.0 © Erdem Bircan