Skip to content

Instantly share code, notes, and snippets.

@NTT123
Created October 19, 2025 09:23
Show Gist options
  • Select an option

  • Save NTT123/2bcdca2fd1f418790ad856385dd3e19c to your computer and use it in GitHub Desktop.

Select an option

Save NTT123/2bcdca2fd1f418790ad856385dd3e19c to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SwiGLU 2D Activation</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #4a4a4a 0%, #2c2c2c 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.container {
background: white;
border-radius: 4px;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
padding: 20px;
max-width: 1200px;
width: 100%;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 5px;
font-size: 24px;
}
.formula {
text-align: center;
color: #666;
margin-bottom: 15px;
font-family: 'Courier New', monospace;
font-size: 14px;
}
.credit {
text-align: center;
color: #999;
margin-top: 10px;
font-size: 11px;
}
.credit a {
color: #667eea;
text-decoration: none;
}
.credit a:hover {
text-decoration: underline;
}
.canvas-container {
display: flex;
justify-content: center;
margin-bottom: 15px;
}
canvas {
border: 2px solid #ddd;
border-radius: 4px;
cursor: grab;
background: #f9f9f9;
max-width: 100%;
height: auto;
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
}
canvas:active {
cursor: grabbing;
}
.controls {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 10px;
margin-bottom: 10px;
}
.control-group {
background: #f5f5f5;
padding: 10px;
border-radius: 4px;
}
.control-group label {
display: block;
margin-bottom: 8px;
color: #555;
font-weight: 600;
font-size: 14px;
}
input[type="range"] {
width: 100%;
margin-bottom: 5px;
}
.value-display {
color: #667eea;
font-weight: bold;
font-size: 14px;
}
.info {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
gap: 10px;
padding: 10px;
background: #f5f5f5;
border-radius: 4px;
}
.info-item {
text-align: center;
}
.info-label {
font-size: 12px;
color: #666;
margin-bottom: 5px;
}
.info-value {
font-size: 18px;
font-weight: bold;
color: #667eea;
}
button {
padding: 10px 20px;
background: #666;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: background 0.3s;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
button:hover {
background: #555;
}
button:active {
transform: scale(0.98);
}
</style>
</head>
<body>
<div class="container">
<h1>SwiGLU 2D Activation</h1>
<div class="formula">z = sigmoid(x) × x × y</div>
<div class="canvas-container">
<canvas id="canvas" width="800" height="500"></canvas>
</div>
<div class="controls">
<div class="control-group">
<label>
Resolution: <span class="value-display" id="resolutionValue">30</span>
</label>
<input type="range" id="resolution" min="10" max="50" value="30" step="5">
</div>
<div class="control-group">
<label>
X Range: <span class="value-display" id="xRangeValue">±5</span>
</label>
<input type="range" id="xRange" min="2" max="10" value="5" step="0.5">
</div>
<div class="control-group">
<label>
Y Range: <span class="value-display" id="yRangeValue">±5</span>
</label>
<input type="range" id="yRange" min="2" max="10" value="5" step="0.5">
</div>
<div class="control-group">
<label>
<button id="resetView">Reset View</button>
</label>
</div>
</div>
<div class="info">
<div class="info-item">
<div class="info-label">Rotation X</div>
<div class="info-value" id="rotX">-45°</div>
</div>
<div class="info-item">
<div class="info-label">Rotation Y</div>
<div class="info-value" id="rotY">30°</div>
</div>
<div class="info-item">
<div class="info-label">Drag to Rotate</div>
<div class="info-value">🖱️</div>
</div>
</div>
<div class="credit">
Implemented with <a href="https://claude.ai/claude-code" target="_blank">Claude Code</a>
</div>
</div>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// Configuration
let config = {
resolution: 30,
xRange: 5,
yRange: 5,
rotationX: -45 * Math.PI / 180, // -45 degrees
rotationY: 30 * Math.PI / 180, // 30 degrees
scale: 40,
offsetX: canvas.width / 2,
offsetY: canvas.height / 2
};
// Mouse interaction
let isDragging = false;
let lastMouseX = 0;
let lastMouseY = 0;
// Sigmoid function
function sigmoid(x) {
return 1 / (1 + Math.exp(-x));
}
// SwiGLU activation function
function swiglu(x, y) {
return sigmoid(x) * x * y;
}
// 3D to 2D projection
function project3D(x, y, z) {
// Apply rotation around X axis
let cosX = Math.cos(config.rotationX);
let sinX = Math.sin(config.rotationX);
let y1 = y * cosX - z * sinX;
let z1 = y * sinX + z * cosX;
// Apply rotation around Y axis
let cosY = Math.cos(config.rotationY);
let sinY = Math.sin(config.rotationY);
let x2 = x * cosY + z1 * sinY;
let z2 = -x * sinY + z1 * cosY;
let y2 = y1;
// Simple orthographic projection
let screenX = x2 * config.scale + config.offsetX;
let screenY = -y2 * config.scale + config.offsetY;
return { x: screenX, y: screenY, z: z2 };
}
// Color based on z value - Improved viridis-like color scheme
function getColor(z, minZ, maxZ) {
// Normalize z to 0-1 range
let normalized = (z - minZ) / (maxZ - minZ);
// Create a smooth gradient: purple → blue → cyan → green → yellow
let r, g, b;
if (normalized < 0.25) {
// Purple to Blue
let t = normalized * 4;
r = Math.floor(68 + t * (31 - 68));
g = Math.floor(1 + t * (120 - 1));
b = Math.floor(84 + t * (180 - 84));
} else if (normalized < 0.5) {
// Blue to Cyan
let t = (normalized - 0.25) * 4;
r = Math.floor(31 + t * (42 - 31));
g = Math.floor(120 + t * (183 - 120));
b = Math.floor(180 + t * (202 - 180));
} else if (normalized < 0.75) {
// Cyan to Green/Yellow-green
let t = (normalized - 0.5) * 4;
r = Math.floor(42 + t * (140 - 42));
g = Math.floor(183 + t * (216 - 183));
b = Math.floor(202 + t * (56 - 202));
} else {
// Yellow-green to Yellow
let t = (normalized - 0.75) * 4;
r = Math.floor(140 + t * (253 - 140));
g = Math.floor(216 + t * (231 - 216));
b = Math.floor(56 + t * (37 - 56));
}
return `rgb(${r}, ${g}, ${b})`;
}
// Generate mesh data
function generateMesh() {
const res = config.resolution;
const xMin = -config.xRange;
const xMax = config.xRange;
const yMin = -config.yRange;
const yMax = config.yRange;
const xStep = (xMax - xMin) / (res - 1);
const yStep = (yMax - yMin) / (res - 1);
let points = [];
let minZ = Infinity;
let maxZ = -Infinity;
// Generate all points
for (let i = 0; i < res; i++) {
points[i] = [];
for (let j = 0; j < res; j++) {
let x = xMin + i * xStep;
let y = yMin + j * yStep;
let z = swiglu(x, y);
minZ = Math.min(minZ, z);
maxZ = Math.max(maxZ, z);
points[i][j] = { x, y, z };
}
}
return { points, minZ, maxZ, res };
}
// Render the 3D surface
function render() {
// Clear canvas
ctx.fillStyle = '#f9f9f9';
ctx.fillRect(0, 0, canvas.width, canvas.height);
const mesh = generateMesh();
const { points, minZ, maxZ, res } = mesh;
// Store all faces with their average z-depth for sorting
let faces = [];
// Create faces (quads)
for (let i = 0; i < res - 1; i++) {
for (let j = 0; j < res - 1; j++) {
let p1 = points[i][j];
let p2 = points[i + 1][j];
let p3 = points[i + 1][j + 1];
let p4 = points[i][j + 1];
// Project all points
let proj1 = project3D(p1.x, p1.y, p1.z);
let proj2 = project3D(p2.x, p2.y, p2.z);
let proj3 = project3D(p3.x, p3.y, p3.z);
let proj4 = project3D(p4.x, p4.y, p4.z);
// Calculate average z for depth sorting
let avgZ = (proj1.z + proj2.z + proj3.z + proj4.z) / 4;
let avgZValue = (p1.z + p2.z + p3.z + p4.z) / 4;
// Checkerboard pattern (alternate dark/light)
let isCheckerDark = (i + j) % 2 === 0;
faces.push({
points: [proj1, proj2, proj3, proj4],
z: avgZ,
color: getColor(avgZValue, minZ, maxZ),
isCheckerDark: isCheckerDark
});
}
}
// Sort faces by depth (painter's algorithm)
faces.sort((a, b) => a.z - b.z);
// Draw faces
faces.forEach(face => {
ctx.beginPath();
ctx.moveTo(face.points[0].x, face.points[0].y);
ctx.lineTo(face.points[1].x, face.points[1].y);
ctx.lineTo(face.points[2].x, face.points[2].y);
ctx.lineTo(face.points[3].x, face.points[3].y);
ctx.closePath();
// Apply checkerboard pattern by modulating the color
let baseColor = face.color;
let rgb = baseColor.match(/\d+/g).map(Number);
// Darken or lighten based on checkerboard pattern
let factor = face.isCheckerDark ? 0.75 : 1.0;
let adjustedColor = `rgb(${Math.floor(rgb[0] * factor)}, ${Math.floor(rgb[1] * factor)}, ${Math.floor(rgb[2] * factor)})`;
// Fill with color
ctx.fillStyle = adjustedColor;
ctx.fill();
// Draw wireframe
ctx.strokeStyle = 'rgba(100, 100, 100, 0.4)';
ctx.lineWidth = 0.5;
ctx.stroke();
});
// Draw axes
drawAxes();
}
// Draw coordinate axes with grid
function drawAxes() {
const axisLength = config.xRange;
const gridStep = 1; // Grid spacing
// Draw grid lines in XY plane
ctx.strokeStyle = 'rgba(200, 200, 200, 0.3)';
ctx.lineWidth = 0.5;
// Grid lines parallel to X axis
for (let y = -axisLength; y <= axisLength; y += gridStep) {
if (y === 0) continue; // Skip the axis line
let start = project3D(-axisLength, y, 0);
let end = project3D(axisLength, y, 0);
ctx.beginPath();
ctx.moveTo(start.x, start.y);
ctx.lineTo(end.x, end.y);
ctx.stroke();
}
// Grid lines parallel to Y axis
for (let x = -axisLength; x <= axisLength; x += gridStep) {
if (x === 0) continue; // Skip the axis line
let start = project3D(x, -axisLength, 0);
let end = project3D(x, axisLength, 0);
ctx.beginPath();
ctx.moveTo(start.x, start.y);
ctx.lineTo(end.x, end.y);
ctx.stroke();
}
// X axis (black)
let xStart = project3D(-axisLength, 0, 0);
let xEnd = project3D(axisLength, 0, 0);
ctx.strokeStyle = 'rgba(0, 0, 0, 0.8)';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(xStart.x, xStart.y);
ctx.lineTo(xEnd.x, xEnd.y);
ctx.stroke();
// Y axis (black)
let yStart = project3D(0, -axisLength, 0);
let yEnd = project3D(0, axisLength, 0);
ctx.strokeStyle = 'rgba(0, 0, 0, 0.8)';
ctx.beginPath();
ctx.moveTo(yStart.x, yStart.y);
ctx.lineTo(yEnd.x, yEnd.y);
ctx.stroke();
// Z axis (black)
let zStart = project3D(0, 0, -axisLength);
let zEnd = project3D(0, 0, axisLength);
ctx.strokeStyle = 'rgba(0, 0, 0, 0.8)';
ctx.beginPath();
ctx.moveTo(zStart.x, zStart.y);
ctx.lineTo(zEnd.x, zEnd.y);
ctx.stroke();
// Draw tick marks and labels
ctx.fillStyle = '#333';
ctx.font = '12px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
// X axis ticks and labels
for (let x = -axisLength; x <= axisLength; x += gridStep) {
let pos = project3D(x, 0, 0);
let tickStart = project3D(x, -0.15, 0);
let tickEnd = project3D(x, 0.15, 0);
// Draw tick mark
ctx.strokeStyle = 'rgba(0, 0, 0, 0.8)';
ctx.lineWidth = 1.5;
ctx.beginPath();
ctx.moveTo(tickStart.x, tickStart.y);
ctx.lineTo(tickEnd.x, tickEnd.y);
ctx.stroke();
// Draw label
if (x % 2 === 0 || axisLength <= 5) { // Show fewer labels for large ranges
let labelPos = project3D(x, -0.5, 0);
ctx.fillStyle = '#000';
ctx.font = 'bold 11px sans-serif';
ctx.fillText(x.toFixed(0), labelPos.x, labelPos.y);
}
}
// Y axis ticks and labels
for (let y = -axisLength; y <= axisLength; y += gridStep) {
let tickStart = project3D(-0.15, y, 0);
let tickEnd = project3D(0.15, y, 0);
// Draw tick mark
ctx.strokeStyle = 'rgba(0, 0, 0, 0.8)';
ctx.lineWidth = 1.5;
ctx.beginPath();
ctx.moveTo(tickStart.x, tickStart.y);
ctx.lineTo(tickEnd.x, tickEnd.y);
ctx.stroke();
// Draw label
if (y % 2 === 0 || axisLength <= 5) {
let labelPos = project3D(-0.5, y, 0);
ctx.fillStyle = '#000';
ctx.font = 'bold 11px sans-serif';
ctx.fillText(y.toFixed(0), labelPos.x, labelPos.y);
}
}
// Z axis ticks and labels
for (let z = -axisLength; z <= axisLength; z += gridStep) {
let tickStart = project3D(-0.15, 0, z);
let tickEnd = project3D(0.15, 0, z);
// Draw tick mark
ctx.strokeStyle = 'rgba(0, 0, 0, 0.8)';
ctx.lineWidth = 1.5;
ctx.beginPath();
ctx.moveTo(tickStart.x, tickStart.y);
ctx.lineTo(tickEnd.x, tickEnd.y);
ctx.stroke();
// Draw label
if (z % 2 === 0 || axisLength <= 5) {
let labelPos = project3D(-0.5, 0, z);
ctx.fillStyle = '#000';
ctx.font = 'bold 11px sans-serif';
ctx.fillText(z.toFixed(0), labelPos.x, labelPos.y);
}
}
// Axis labels (X, Y, Z)
ctx.font = 'bold 16px sans-serif';
ctx.fillStyle = '#000';
ctx.fillText('X', xEnd.x + 15, xEnd.y);
ctx.fillText('Y', yEnd.x + 15, yEnd.y);
ctx.fillText('Z', zEnd.x + 15, zEnd.y);
}
// Update rotation display
function updateRotationDisplay() {
document.getElementById('rotX').textContent =
Math.round(config.rotationX * 180 / Math.PI) + '°';
document.getElementById('rotY').textContent =
Math.round(config.rotationY * 180 / Math.PI) + '°';
}
// Mouse event handlers
canvas.addEventListener('mousedown', (e) => {
isDragging = true;
lastMouseX = e.clientX;
lastMouseY = e.clientY;
});
document.addEventListener('mousemove', (e) => {
if (isDragging) {
const deltaX = e.clientX - lastMouseX;
const deltaY = e.clientY - lastMouseY;
config.rotationY += deltaX * 0.01;
config.rotationX += deltaY * 0.01;
lastMouseX = e.clientX;
lastMouseY = e.clientY;
updateRotationDisplay();
render();
}
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
// Control event listeners
document.getElementById('resolution').addEventListener('input', (e) => {
config.resolution = parseInt(e.target.value);
document.getElementById('resolutionValue').textContent = config.resolution;
render();
});
document.getElementById('xRange').addEventListener('input', (e) => {
config.xRange = parseFloat(e.target.value);
document.getElementById('xRangeValue').textContent = '±' + config.xRange;
render();
});
document.getElementById('yRange').addEventListener('input', (e) => {
config.yRange = parseFloat(e.target.value);
document.getElementById('yRangeValue').textContent = '±' + config.yRange;
render();
});
document.getElementById('resetView').addEventListener('click', () => {
config.rotationX = -45 * Math.PI / 180;
config.rotationY = 30 * Math.PI / 180;
config.resolution = 30;
config.xRange = 5;
config.yRange = 5;
document.getElementById('resolution').value = 30;
document.getElementById('resolutionValue').textContent = '30';
document.getElementById('xRange').value = 5;
document.getElementById('xRangeValue').textContent = '±5';
document.getElementById('yRange').value = 5;
document.getElementById('yRangeValue').textContent = '±5';
updateRotationDisplay();
render();
});
// Initial render
updateRotationDisplay();
render();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment