Site Fix Notes: 3D Rubik Orientation, Solution Formula, and Drag Limits
This fix unifies the 3D Rubik puzzle's screen and logical axes, restores a repeatable initial state, expands free-view dragging, and reduces its focused size.
The Rubik issue was not one isolated keyboard bug. It appeared as several symptoms that looked unrelated: after rotating the view, the keyboard still seemed to address the same fixed face; refresh and re-entry did not reliably restore the initial state; the displayed solution formula did not undo the visible scramble; and free-view dragging eventually felt as if it hit an invisible wall.
They all came from the boundary between presentation coordinates and logical puzzle coordinates.
Two Coordinate Systems Produced Two Answers
The Blender cube does not arrive in the browser with an identity orientation. The GLB includes transforms on both StudioObject_RubikCube and Rubik_ViewRoot, and the focused presentation adds another screen-centred frame.
Initialization previously applied the scramble on fixed logical axes, while active keyboard input interpreted F/B, R/L, and U/D from the current camera. The same notation therefore had two meanings: one during initialization and another during interaction. An inverse test built only around identity matrices could prove the algebra while completely missing the delivered model orientation.
The controller now requires the room camera and is created only after the camera and GLB hierarchy are ready. Both the initial R U r u scramble and later keyboard moves pass through the same current-view resolver. In the initial presented orientation, the displayed U R u r sequence is now the real inverse.
The Initial State Belongs to One Visit
The puzzle had also persisted moves in browser storage. That did not match the gallery experience: refreshing or returning to the object should present the same explainable, testable starting puzzle instead of an unfinished state from an earlier visit.
The controller now keeps two kinds of in-memory snapshot:
- An immutable initial scramble and Blender inspection orientation, used after a completed exit, re-entry, refresh, recreation, or disposal.
- A last stable snapshot for the current attempt, updated only after settled input and used to roll back an interrupted turn or cancelled pointer.
Rubik state is no longer read from or written to localStorage. Reset now has one meaning, and obsolete data from an earlier build cannot change it.
The Drag Lock Happened at 90 Degrees
Free-view rotation already used the settled quaternion at pointer-down as its baseline. That accumulation was intact. The actual problem was a clamp: horizontal and vertical intent were each limited to ±π/2, or 90 degrees. Once the pointer displacement reached that value, further movement could not change the result, which felt like a permanent lock.
Each gesture now permits ±4π on both screen axes—two complete turns. Releasing commits the current quaternion as the next gesture's baseline, so rotation can continue naturally while retaining a finite guard against abnormal pointer displacement.
The focused cube is also slightly smaller. Its dynamic fit target changed from 0.60 × 0.60 to 0.54 × 0.54 of the viewport. With the current 16:9 camera and real model bounds, it projects at roughly 32.36% × 57.54%: still centred and uncropped, with more room for guidance and the surrounding composition.
Verify the Delivered Asset, Not Only the Formula
The new regression does not stop at a synthetic puzzle. It reads the manifest-selected production GLB, restores the real camera, outer root, Rubik_ViewRoot, and all 26 cubie transforms, then runs the solution through the same view-aware path as production. Every cubie's position, quaternion, and scale must return to its authored pose.
Free-view dragging has a deterministic regression too: pointer displacement equivalent to two full turns must reach ±4π on both axes instead of saturating at 90 degrees.
The broader lesson is simple. When an interaction depends on exported model transforms, camera direction, or screen-relative meaning, identity-coordinate tests are not enough. Initialization, live input, and user-facing guidance must share the same coordinate authority, or each part can look correct by itself while the complete interaction remains impossible to use.

