Tesseract Portal — 4D Hypercube Projection
52 views · Dec 15, 2025
A rotating tesseract (4D hypercube) projected into 3D WEBGL space. Drag to steer the rotation, click to toggle auto-rotate, and press R to randomize the spin. Wireframe edges are drawn with additive glow for a portal-like feel. Created with xelsed-alpha6-flash.
📋 Embed Code
Copy this code to embed this sketch on your website:
<iframe src="https://p5js.ai/embed/431e46a1-7a59-40ff-a99b-1c852131d30e" width="400" height="400" frameborder="0" allow="autoplay" title="Tesseract Portal — 4D Hypercube Projection"></iframe>
Adjust width and height as needed. Preview embed →
❓ About This Sketch 3 questions
What visual effect does the Tesseract Portal sketch create?
The sketch visually represents a rotating tesseract (4D hypercube) projected into 3D space, featuring glowing wireframe edges that enhance its portal-like appearance.
How can users interact with the Tesseract Portal sketch?
Users can drag to steer the rotation, click to toggle auto-rotation, and press 'R' to randomize the spin of the tesseract.
What creative coding concepts does the Tesseract Portal sketch demonstrate?
This sketch demonstrates 4D rotation projections into 3D space and the use of WebGL for rendering dynamic, interactive visualizations.
💬 AI Chat History 4 messages
The core ideas will be:
1. **4D Vertex Generation:** Create the 16 vertices of a tesseract by systematically assigning -1 or +1 to each of its four coordinates (x, y, z, w).
2. **Edge Definition:** Determine which pairs of vertices are connected by an edge. This occurs when two vertices differ in exactly one coordinate.
3. **4D Rotation:** Apply 4D rotation matrices to the vertices. We'll use two planes (x-w and y-z) as requested, combining their rotation matrices.
4. **Perspective Projection:** Project the rotated 4D vertices into 3D space. The `w` coordinate will be used for perspective scaling, effectively moving vertices "closer" or "further" based on their 4D depth.
5. **3D Rendering:** Draw lines between the projected 3D vertices using p5.js's `WEBGL` mode. We'll implement the "additive glow" by drawing each edge twice with different stroke properties and `blendMode(ADD)`.
6. **Interaction & UI:** Add mouse drag for speed/direction, click for auto-rotate, 'R' for random speeds, and an on-canvas 2D overlay for instructions and current settings.
---
### Step 1: `index.html`
We need to load p5.js, its sound addon (good practice even if not used, as it's a common addon), and our `sketch.js` file. We'll also link to `style.css`.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>4D Hypercube Visualization</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- Load p5.js core library -->
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
<!-- Load p5.sound addon (even if not used, good practice as it's a common bundled addon) -->
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/addons/p5.sound.min.js"></script>
<!-- Load our sketch -->
<script src="sketch.js"></script>
</body>
</html>
```
---
### Step 2: `style.css`
Simple CSS to remove default margins and ensure the canvas is a block element.
```css
body {
margin: 0;
padding: 0;
overflow: hidden; /* Prevent scrollbars */
background-color: black; /* Set background for the page */
}
canvas {
display: block; /* Remove extra space below canvas */
}
```
---
### Step 3: `sketch.js`
This will be the main file containing all the logic.
```js
// --- Global Variables ---
let vertices4D = []; // Stores the original 4D vertices
let edges = []; // Stores pairs of indices that form an edge
let angleXW = 0; // Rotation angle for the x-w plane
let angleYZ = 0; // Rotation angle for the y-z plane
let rotationSpeedXW = 0.01; // Speed of rotation in x-w plane
let rotationSpeedYZ = 0.015; // Speed of rotation in y-z plane
let autoRotate = true; // Toggle auto-rotation
let hypercubeSize = 150; // Scaling factor for the hypercube display
let projectionDistance = 2; // Distance from the viewer for perspective projection
let myFont; // Variable to store the loaded font
// --- Preload Font ---
// In WEBGL mode, text requires a loaded font.
// Using Fontsource CDN for "Source Code Pro".
function preload() {
myFont = loadFont('https://unpkg.com/@fontsource/source-code-pro@latest/files/source-code-pro-latin-400-normal.woff');
}
// --- Setup Function ---
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
noFill();
stroke(255);
strokeWeight(2);
textFont(myFont); // Set the loaded font for text
textSize(16);
generateHypercube(); // Generate vertices and edges
}
// --- Draw Function ---
function draw() {
background(0); // Black background
// Update rotation angles if auto-rotate is on
if (autoRotate) {
angleXW += rotationSpeedXW;
angleYZ += rotationSpeedYZ;
}
// --- 4D Rotation ---
// Create rotation matrices for x-w and y-z planes
// Matrix for x-w plane rotation:
// [ cos(a) 0 0 -sin(a) ]
// [ 0 1 0 0 ]
// [ 0 0 1 0 ]
// [ sin(a) 0 0 cos(a) ]
const matXW = [
[cos(angleXW), 0, 0, -sin(angleXW)],
[0, 1, 0, 0],
[0, 0, 1, 0],
[sin(angleXW), 0, 0, cos(angleXW)]
];
// Matrix for y-z plane rotation:
// [ 1 0 0 0 ]
// [ 0 cos(a) -sin(a) 0 ]
// [ 0 sin(a) cos(a) 0 ]
// [ 0 0 0 1 ]
const matYZ = [
[1, 0, 0, 0],
[0, cos(angleYZ), -sin(angleYZ), 0],
[0, sin(angleYZ), cos(angleYZ), 0],
[0, 0, 0, 1]
];
// Combine rotations by multiplying matrices (order matters!)
// Applying YZ rotation, then XW rotation
const combinedMatrix = multiplyMatrices(matXW, matYZ);
// Apply the combined rotation to each 4D vertex
let rotatedVertices4D = vertices4D.map(v => multiplyMatrixVector(combinedMatrix, v));
// --- 4D to 3D Perspective Projection ---
// Project each rotated 4D vertex to 3D space
// Perspective projection based on 'w' coordinate:
// x_proj = x / (distance - w)
// y_proj = y / (distance - w)
// z_proj = z / (distance - w)
let projectedVertices3D = rotatedVertices4D.map(v => {
// Avoid division by zero or very small numbers
let scaleFactor = 1 / (projectionDistance - v[3]);
return createVector(
v[0] * scaleFactor * hypercubeSize,
v[1] * scaleFactor * hypercubeSize,
v[2] * scaleFactor * hypercubeSize
);
});
// --- Render Hypercube Wireframe ---
// Draw edges with a base stroke
stroke(255); // White stroke
strokeWeight(1);
beginShape(LINES);
for (let edge of edges) {
let v1 = projectedVertices3D[edge[0]];
let v2 = projectedVertices3D[edge[1]];
vertex(v1.x, v1.y, v1.z);
vertex(v2.x, v2.y, v2.z);
}
endShape();
// Draw edges again with additive blend mode for glow effect
blendMode(ADD); // Switch to additive blending
stroke(0, 255, 255, 50); // Cyan stroke with some transparency
strokeWeight(3);
beginShape(LINES);
for (let edge of edges) {
let v1 = projectedVertices3D[edge[0]];
let v2 = projectedVertices3D[edge[1]];
vertex(v1.x, v1.y, v1.z);
vertex(v2.x, v2.y, v2.z);
}
endShape();
blendMode(BLEND); // Reset blend mode to default
// --- Draw UI Text (2D Overlay) ---
// In WEBGL mode, to draw 2D text on top, we switch to 2D projection using ortho()
push();
ortho(); // Switch to 2D orthographic projection
translate(-width / 2, -height / 2); // Move origin to top-left for 2D UI
fill(255); // White text
noStroke(); // No stroke for text
text("Controls:", 20, 30);
text("Drag mouse: Change rotation speed", 20, 50);
text("Click: Toggle auto-rotate (" + (autoRotate ? "ON" : "OFF") + ")", 20, 70);
text("Press 'R': Randomize rotation speeds", 20, 90);
text("Rotation Speed XW: " + rotationSpeedXW.toFixed(4), 20, 120);
text("Rotation Speed YZ: " + rotationSpeedYZ.toFixed(4), 20, 140);
pop(); // Restore 3D projection
}
// --- Event Handlers ---
function mousePressed() {
autoRotate = !autoRotate; // Toggle auto-rotate on click
}
function mouseDragged() {
// Map mouse velocity to rotation speed changes
let dx = mouseX - pmouseX;
let dy = mouseY - pmouseY;
rotationSpeedXW += dx * 0.0001;
rotationSpeedYZ += dy * 0.0001;
// Clamp speeds to a reasonable range
rotationSpeedXW = constrain(rotationSpeedXW, -0.05, 0.05);
rotationSpeedYZ = constrain(rotationSpeedYZ, -0.05, 0.05);
}
function keyPressed() {
// Randomize rotation speeds on 'R' press
if (key === 'R' || key === 'r') {
rotationSpeedXW = random(-0.02, 0.02);
rotationSpeedYZ = random(-0.02, 0.02);
}
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}
// --- Helper Functions ---
// Generates the 16 vertices of a 4D hypercube and defines its edges.
function generateHypercube() {
vertices4D = [];
edges = [];
// Generate 16 vertices (2^4)
// Each coordinate (x,y,z,w) can be -1 or +1
for (let i = 0; i < 16; i++) {
let v = [];
v[0] = (i & 1) ? 1 : -1; // x coordinate
v[1] = (i & 2) ? 1 : -1; // y coordinate
v[2] = (i & 4) ? 1 : -1; // z coordinate
v[3] = (i & 8) ? 1 : -1; // w coordinate
vertices4D.push(v);
}
// Define edges: an edge connects two vertices that differ by exactly one coordinate
for (let i = 0; i < vertices4D.length; i++) {
for (let j = i + 1; j < vertices4D.length; j++) {
let diffCount = 0;
for (let k = 0; k < 4; k++) {
if (vertices4D[i][k] !== vertices4D[j][k]) {
diffCount++;
}
}
if (diffCount === 1) {
edges.push([i, j]); // Store indices of connected vertices
}
}
}
}
// Multiplies a 4x4 matrix by a 4D vector.
// Parameters:
// matrix: A 2D array representing the 4x4 matrix.
// vector: A 1D array representing the 4D vector [x, y, z, w].
// Returns: A new 1D array representing the rotated 4D vector.
function multiplyMatrixVector(matrix, vector) {
let result = [0, 0, 0, 0];
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
result[i] += matrix[i][j] * vector[j];
}
}
return result;
}
// Multiplies two 4x4 matrices.
// Parameters:
// m1: The first 4x4 matrix (2D array).
// m2: The second 4x4 matrix (2D array).
// Returns: A new 2D array representing the resulting 4x4 matrix.
function multiplyMatrices(m1, m2) {
let result = Array(4).fill(0).map(() => Array(4).fill(0));
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
for (let k = 0; k < 4; k++) {
result[i][j] += m1[i][k] * m2[k][j];
}
}
}
return result;
}
```
---
### How to Use and Interact:
1. **Save the files:** Make sure you have `index.html`, `style.css`, and `sketch.js` in the same folder.
2. **Open `index.html`:** Open `index.html` in your web browser.
3. **Explore the hypercube:**
* **Auto-rotate:** The hypercube will start rotating automatically.
* **Toggle Auto-rotate:** Click anywhere on the canvas to turn auto-rotation ON or OFF.
* **Change Speed/Direction:** Click and drag your mouse on the canvas. Moving left/right changes the x-w plane rotation speed, and moving up/down changes the y-z plane rotation speed.
* **Random Speeds:** Press the 'R' key to randomize both rotation speeds.
This sketch provides a compelling visualization of a 4D hypercube, allowing you to manipulate its rotations and observe how its 3D projection changes. Enjoy exploring higher dimensions!
---
```js
// sketch.js
// --- Global Variables ---
let vertices4D = []; // Original 4D vertices
let edges = []; // Pairs of indices forming edges
let angleXW = 0; // Rotation angle for x-w plane
let angleYZ = 0; // Rotation angle for y-z plane
let rotationSpeedXW = 0.01;
let rotationSpeedYZ = 0.015;
let autoRotate = true; // Toggle auto-rotation
let hypercubeSize = 180; // Scale of hypercube in 3D
let projDist = 3; // Projection distance for 4D -> 3D
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
noFill();
stroke(255);
strokeWeight(1);
buildHypercubeVertices();
buildHypercubeEdges();
}
function draw() {
background(0);
// Update rotation angles
if (autoRotate) {
angleXW += rotationSpeedXW;
angleYZ += rotationSpeedYZ;
}
// Rotate and project vertices
const projected = [];
for (let i = 0; i < vertices4D.length; i++) {
const vRot = rotate4D(vertices4D[i]);
projected.push(project4Dto3D(vRot));
}
// --- Draw wireframe with additive glow ---
blendMode(ADD);
// First pass: thick, low-alpha cyan glow
stroke(0, 255, 255, 60);
strokeWeight(4);
for (let k = 0; k < edges.length; k++) {
const a = projected[edges[k][0]];
const b = projected[edges[k][1]];
line(a.x, a.y, a.z, b.x, b.y, b.z);
}
// Second pass: thin, bright white core lines
stroke(255);
strokeWeight(1.2);
for (let k = 0; k < edges.length; k++) {
const a = projected[edges[k][0]];
const b = projected[edges[k][1]];
line(a.x, a.y, a.z, b.x, b.y, b.z);
}
blendMode(BLEND);
// --- 2D UI overlay in WEBGL ---
push();
resetMatrix(); // Reset transform
translate(-width / 2, -height / 2, 0); // Move origin to top-left
hint(DISABLE_DEPTH_TEST); // Ensure UI draws on top
fill(255);
noStroke();
textSize(12);
text('drag: change speed | click: toggle auto | R: randomize', 12, 18);
const speedText =
'speedXW=' + nf(rotationSpeedXW, 1, 4) +
' speedYZ=' + nf(rotationSpeedYZ, 1, 4);
text(speedText, 12, 36);
hint(ENABLE_DEPTH_TEST);
pop();
}
// --- Interaction ---
function mouseDragged() {
// Use mouse movement to tweak rotation speeds
rotationSpeedXW += movedX * 0.0002;
rotationSpeedYZ += movedY * 0.0002;
// Clamp speeds
rotationSpeedXW = constrain(rotationSpeedXW, -0.05, 0.05);
rotationSpeedYZ = constrain(rotationSpeedYZ, -0.05, 0.05);
}
function mousePressed() {
autoRotate = !autoRotate;
}
function keyPressed() {
if (key === 'R' || key === 'r') {
rotationSpeedXW = random(-0.03, 0.03);
rotationSpeedYZ = random(-0.03, 0.03);
}
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}
// --- Hypercube construction ---
function buildHypercubeVertices() {
vertices4D = [];
for (let i = 0; i < 16; i++) {
vertices4D.push({
x: (i & 1) ? 1 : -1,
y: (i & 2) ? 1 : -1,
z: (i & 4) ? 1 : -1,
w: (i & 8) ? 1 : -1
});
}
}
function buildHypercubeEdges() {
edges = [];
// Edges between vertices that differ by exactly one bit
for (let i = 0; i < 16; i++) {
for (let b = 0; b < 4; b++) {
let j = i ^ (1 << b); // Flip bit b
if (i < j) {
edges.push([i, j]);
}
}
}
}
// --- 4D rotation and projection ---
// Apply two 4D rotations: x-w plane and y-z plane
function rotate4D(v) {
const cw = cos(angleXW);
const sw = sin(angleXW);
const cz = cos(angleYZ);
const sz = sin(angleYZ);
// Rotation in x-w plane
const x2 = v.x * cw - v.w * sw;
const w2 = v.x * sw + v.w * cw;
// Rotation in y-z plane
const y2 = v.y * cz - v.z * sz;
const z2 = v.y * sz + v.z * cz;
return { x: x2, y: y2, z: z2, w: w2 };
}
// 4D -> 3D perspective projection using w
function project4Dto3D(v) {
const denom = max(0.2, projDist - v.w);
const s = 1 / denom;
return {
x: v.x * s * hypercubeSize,
y: v.y * s * hypercubeSize,
z: v.z * s * hypercubeSize
};
}
```
---
```html
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>4D Hypercube Visualization</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- Load p5.js core library -->
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
<!-- Load p5.sound addon (allowed but not required for this sketch) -->
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/addons/p5.sound.min.js"></script>
<!-- Load our sketch -->
<script src="sketch.js"></script>
</body>
</html>
```
---
```css
/* style.css */
body {
margin: 0;
padding: 0;
overflow: hidden; /* Prevent scrollbars */
background-color: black;
}
canvas {
display: block; /* Remove extra space below canvas */
}
```
This should give you a centered, rotating tesseract wireframe with additive glow and fully working interactions (drag, click, R) and overlay UI, with no external assets or preload.