TwistySim

An interactive HTML5 twisty puzzle simulator

To see TwistySim in action, check out this demonstrator app.

Download the latest version here:

cube.crider.co.uk/twistysim.js

TwistySim JavaScript API

To use TwistySim in your web-page, simply include twistysim.js in a <script> tag, and generate the images using the API calls described below:

TwistyPuzzle

This is the base puzzle rendering class. It is used by all other classes to render puzzle images. Use it directly to render static puzzle images.

Basic render into div with selector


TTk.TiwstyPuzzle(3)('#tp1');
						

Custom size and face definition


TTk.TwistyPuzzle(2)
	.size({width:100, height:150})
	.fc('wwwwrddrdbbddyyd')
	('#tp2');
						

Manipulation of 3D transform


// Initialise
var cube = TTk.TwistyPuzzle('pyraminx');
// Adjust rendering context
cube.context().transform()
	.pitch(1.4)
	.yaw(0.2)
	.scale(1.3);
// Render into div
cube('#tp3');
						

Manipulation of 3D projection


// Initialise
var cube = TTk.TwistyPuzzle('skewb');
// Adjust rendering context
cube.context().projection()
	.focalFac(1.5);
// Render into div
cube('#tp4');
						

Application of algorithm


TTk.TwistyPuzzle(3)
	.alg("M2 E2 S2")
	('#tp5');
						

Random scramble


TTk.TwistyPuzzle('megaminx')
	.scramble()
	('#tp6');
						

InteractivePuzzle

A twisty puzzle visualisation that can be drag-rotated and manipulated with mouse clicks or keyboard strokes.

The InteractivePuzzle API supports the same functions as TwistyPuzzle, with added features related to interactivity.

Basic render into div with selector

drag to rotate, click to apply moves
Once focused, use enter to scramble and other keyboard keys to move


TTk.InteractivePuzzle(3)('#ip1');
						

Adjustment of animation speed


TTk.InteractivePuzzle('square1')
	.movePeriod(300)
	.scramblePeriod(1000)
	('#ip2');
						

Toggling interaction features


var cube = TTk.InteractivePuzzle(4)
	// Enable/disable scene rotation
	.rotate(false);
cube.moveInteract()
	// Set whether to use mouse clicks to move
	.mouse(false)
	// Set whether to use keystrokes to move
	.keyboard(true);
// Render
cube('#ip3');
						

AlgorithmPuzzle

A twisty puzzle visualisation that plays a given algorithm, and can be drag-rotated.

Basic render with algorithm


TTk.AlgorithmPuzzle(3)
	.alg("R U R' U R U2 R'")
	('#ap1');
						

Render without controls


TTk.AlgorithmPuzzle(6)
	.case("r u r' u r u2' r'")
	.controls(false)
	('#ap2');
						

Toggle other features


TTk.AlgorithmPuzzle('megaminx')
	// Set the alg to apply
	.case("R U R' U R U3 R'")
	// Whether to display the algorithm
	.showAlg(true)
	// Whether to show buttons on hover
	.hoverButtons(false)
	// Whether to show alg on hover
	.hoverAlg(false)
	('#ap3');
						

Programmatic control

The algorithm puzzle can be controlled using the following JavaScript calls:


// Create and render the instance
var cube = TTk.AlgorithmPuzzle(3)
	.alg("R U R' U R U2 R'")
	('#ap4');
// Play from the beginning
cube.play();
// Pause if currently playing
cube.pause();
// Step forward in the sequence
cube.forward();
// Step back in the sequence
cube.back();
// Move to the end of the sequence
cube.end();
// Move to the start of the sequence
cube.rewind();
// Move to a specific point in the sequence
cube.moveTo(4);
						

ModalPuzzle

Any of the above twisty puzzle classes displayed in a full-screen modal box.

Modal for InteractivePuzzle


var c = TTk.InteractivePuzzle('skewb');
TTk.ModalPuzzle(c)('#mp1');
						

Modal from AlgorithmPuzzle


var s = TTk.AlgorithmPuzzle('square1')
	.alg("U D' / U3 D3 / D6 / U3 D3 /");
TTk.ModalPuzzle(s)('#mp2');
						

Puzzle Definitions

TwistySim uses a twisty puzzle definition object to generate each puzzle type, allowing the system to simulate any twisty puzzle. Built in definitions are provided for all twisty puzzles used in WCA competitions. As an example, the definition for the Skewb puzzle has been provided below:


// The puzzle is defined as an object on the TTk.Puzzle namespace
TTk.Puzzle.SKEWB = {};
// 3D coordinates for the panels that make up the puzzle
// The puzzle is defined within a coordinate space ranging
// from -0.5 to 0.5 in all dimensions
TTk.Puzzle.SKEWB.panels = [
	[[-.0, -.5, -.5], [-.5, -.0, -.5], [-.0, +.5, -.5], [+.5, -.0, -.5]],
	[[-.0, -.5, -.5], [-.5, -.0, -.5], [-.5, -.5, -.5]],
	[[-.5, +.0, -.5], [-.0, +.5, -.5], [-.5, +.5, -.5]],
	[[+.0, +.5, -.5], [+.5, +.0, -.5], [+.5, +.5, -.5]],
	[[+.5, -.0, -.5], [+.0, -.5, -.5], [+.5, -.5, -.5]],

	[[-.0, -.5, +.5], [-.5, -.0, +.5], [-.0, +.5, +.5], [+.5, -.0, +.5]],
	[[-.0, -.5, +.5], [-.5, -.0, +.5], [-.5, -.5, +.5]],
	[[-.5, +.0, +.5], [-.0, +.5, +.5], [-.5, +.5, +.5]],
	[[+.0, +.5, +.5], [+.5, +.0, +.5], [+.5, +.5, +.5]],
	[[+.5, -.0, +.5], [+.0, -.5, +.5], [+.5, -.5, +.5]],

	[[+.0, -.5, -.5], [+.5, -.5, -.0], [+.0, -.5, +.5], [-.5, -.5, +.0]],
	[[+.0, -.5, -.5], [+.5, -.5, -.0], [+.5, -.5, -.5]],
	[[+.0, -.5, +.5], [+.5, -.5, +.0], [+.5, -.5, +.5]],
	[[-.0, -.5, -.5], [-.5, -.5, -.0], [-.5, -.5, -.5]],
	[[-.0, -.5, +.5], [-.5, -.5, +.0], [-.5, -.5, +.5]],

	[[+.0, +.5, -.5], [+.5, +.5, -.0], [+.0, +.5, +.5], [-.5, +.5, +.0]],
	[[+.0, +.5, -.5], [+.5, +.5, -.0], [+.5, +.5, -.5]],
	[[+.0, +.5, +.5], [+.5, +.5, +.0], [+.5, +.5, +.5]],
	[[-.0, +.5, -.5], [-.5, +.5, -.0], [-.5, +.5, -.5]],
	[[-.0, +.5, +.5], [-.5, +.5, +.0], [-.5, +.5, +.5]],

	[[+.5, -.0, -.5], [+.5, +.5, -.0], [+.5, -.0, +.5], [+.5, -.5, +.0]],
	[[+.5, -.0, -.5], [+.5, -.5, -.0], [+.5, -.5, -.5]],
	[[+.5, -.5, +.0], [+.5, -.0, +.5], [+.5, -.5, +.5]],
	[[+.5, +.0, +.5], [+.5, +.5, +.0], [+.5, +.5, +.5]],
	[[+.5, +.5, -.0], [+.5, +.0, -.5], [+.5, +.5, -.5]],

	[[-.5, -.0, -.5], [-.5, +.5, -.0], [-.5, -.0, +.5], [-.5, -.5, +.0]],
	[[-.5, -.0, -.5], [-.5, -.5, -.0], [-.5, -.5, -.5]],
	[[-.5, -.5, +.0], [-.5, -.0, +.5], [-.5, -.5, +.5]],
	[[-.5, +.0, +.5], [-.5, +.5, +.0], [-.5, +.5, +.5]],
	[[-.5, +.5, -.0], [-.5, +.0, -.5], [-.5, +.5, -.5]]
];
pnl = TTk.Puzzle.SKEWB.panels;
// Define unique faces, the face's plane is used to detect solved state
TTk.Puzzle.SKEWB.faces = {
	U:pnl[0],
	D:pnl[5],
	F:pnl[10],
	B:pnl[15],
	R:pnl[20],
	L:pnl[25]
};
// Define moves
TTk.Puzzle.SKEWB.moves = {
	'L': {plane: [pnl[20][0], pnl[20][3], pnl[25][2], pnl[25][1]], angle:-120, type: T.ABOVE},
	'R': {plane: [pnl[0][0], pnl[0][1], pnl[5][2], pnl[5][3]], angle:120, type: T.ABOVE},
	'B': {plane: [pnl[0][3], pnl[0][0], pnl[5][2], pnl[5][1]], angle:120, type: T.ABOVE},
	'U': {plane: [pnl[5][2], pnl[5][1], pnl[0][0], pnl[0][3]], angle:-120, type: T.ABOVE},
	'r': {plane: [pnl[25][1], pnl[25][2], pnl[20][3], pnl[20][0]], angle:-120, type: T.ABOVE},
	'l': {plane: [pnl[5][3], pnl[5][2],  pnl[0][1], pnl[0][0]], angle:-120, type: T.ABOVE},
	'b': {plane: [pnl[5][0], pnl[5][3], pnl[0][2], pnl[0][1]], angle:-120, type: T.ABOVE},
	'f': {plane: [pnl[0][1], pnl[0][2], pnl[5][3], pnl[5][0]], angle:-120, type: T.ABOVE},
	'y': {plane: [pnl[1][2], pnl[2][2], pnl[3][2], pnl[4][2]], angle:90, type: T.ROTATE},
	'z': {plane: [pnl[16][2], pnl[17][2], pnl[18][2], pnl[19][2]], angle:90, type: T.ROTATE},
	'x': {plane: [pnl[26][2], pnl[27][2], pnl[28][2], pnl[29][2]], angle:90, type: T.ROTATE}
};
// Panels to use for mouse-activated moves
TTk.Puzzle.SKEWB.panelMoves = {
	'L' : [13, 1, 26],
	'B' : [2, 29, 18],
	'R' : [24, 16, 3],
	'U' : [22, 12, 9],
	'f' : [11, 21, 4],
	'r' : [17, 8, 23],
	'l' : [14, 6, 27],
	'b' : [7, 28, 19],
};
// Move-keyboard bindings
TTk.Puzzle.SKEWB.keys = {
	73: "R"  ,
	75: "R'" ,
	87: "B"  ,
	79: "B'" ,
	83: "D"  ,
	76: "D'" ,
	68: "L"  ,
	69: "L'" ,
	74: "U"  ,
	70: "U'" ,
	72: "F"  ,
	71: "F'" ,
	78: "F"  ,
	86: "F'" ,
	67: "l",
	82: "l'",
	85: "r",
	77: "r'",
	84: "x"  ,
	89: "x"  ,
	66: "x'" ,
	186:"y"  ,
	59: "y"  ,
	65: "y'" ,
	80: "z"  ,
	81: "z'"
};
// Colours to use for facelets
TTk.Puzzle.SKEWB.palette = {
	'y': '#E6D223', // Yellow
	'r': '#7F0000', // Red
	'o': '#DF622D', // Orange
	'b': '#010080', // Blue
	'g': '#008001', // Green
	'w': '#FFFFFF' // White
};
// Colours to set on each facelet
TTk.Puzzle.SKEWB.fc = "yyyyywwwwwbbbbbgggggooooorrrrr";
// Initial rotation
TTk.Puzzle.SKEWB.rotation = { pitch:30, yaw:45 };