Show description
The Art of Mathematics: Exploring Hamid Naderi Yeganeh's Work
The Art of Mathematics: Exploring Hamid Naderi Yeganeh's Work
Math is Beautiful
An interactive exploration of the work of mathematical artist Hamid Naderi Yeganeh.
The Artist and The Process
Hamid Naderi Yeganeh, an Iranian mathematician and artist, discovered that he could find "interesting shapes that resemble real things" by using mathematical formulas. By writing computer programs, he can generate thousands of unique, intricate images based on a single formula, simply by changing a few parameters.
His process is one of exploration. He often doesn't know what an equation will produce until he runs the program. Finding recognizable objects, like a boat or animals, can be a happy accident after weeks of tweaking formulas. He shares his formulas openly to demonstrate the creative power and beauty inherent in mathematics.
∫ f(x) dx
The Gallery & The Math
Select an artwork to see how it's made. Use the sliders to manipulate the mathematical formula in real time and see how the image changes. This section reveals the direct connection between numbers and intricate beauty.
A Bird in Flight
Boat
Cosmic Circles
Select an artwork to see its formula.
Create Your Own
Now it's your turn. Use the editor below to write your own drawing logic using JavaScript. The code runs in a loop from k=0 to a specified count. You can use the parameters `p1` to `p4` from the sliders. Press 'Generate' to see your creation.
Generate Artwork
Inspired by the work of Hamid Naderi Yeganeh. Application created for educational purposes.
The Art of Mathematics: Exploring Hamid Naderi Yeganeh's Work
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>The Art of Mathematics: Exploring Hamid Naderi Yeganeh's Work</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap" rel="stylesheet">
<!-- Chosen Palette: Warm Neutrals with Teal Accent -->
<!-- Application Structure Plan: The application is structured as a narrative journey to directly address the user's query. It begins with an introduction to the artist (The Who), then delves into an interactive gallery that deconstructs his work (The Math), and culminates in a creative sandbox (The How-To). This top-down, thematic structure is more intuitive for learning than a simple recreation of the article's layout. It guides the user from inspiration, to understanding, to creation, providing a complete and engaging experience. Key interactions include selecting artworks to render them on a canvas and manipulating formula parameters with sliders to see the art change in real-time. -->
<!-- Visualization & Content Choices: The core of the application is a custom interactive visualization tool, not a standard chart. Report Info: Yeganeh's artworks ("Bird", "Boat", "Circles") created from formulas. Goal: Allow users to understand the connection between math and art. Viz/Presentation Method: An HTML Canvas element is used to render recreations of the artworks. This is superior to static images as it allows for dynamic regeneration. Interaction: Users select an artwork via buttons. The corresponding formula is displayed, and sliders allow real-time manipulation of the formula's key parameters. The canvas updates instantly. Justification: This direct manipulation provides a powerful, hands-on learning experience that text alone cannot. It directly answers the user's question about "the math he does". Library/Method: Vanilla JavaScript with the 2D Canvas API is used for maximum flexibility in rendering these custom mathematical shapes. -->
<!-- CONFIRMATION: NO SVG graphics used. NO Mermaid JS used. -->
<style>
body {
font-family: 'Inter', sans-serif;
}
.chart-container {
position: relative;
width: 100%;
height: 60vh;
max-height: 500px;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
input[type=range] {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 8px;
background: #d1d5db;
border-radius: 5px;
outline: none;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
background: #0d9488;
cursor: pointer;
border-radius: 50%;
}
input[type=range]::-moz-range-thumb {
width: 20px;
height: 20px;
background: #0d9488;
cursor: pointer;
border-radius: 50%;
}
</style>
</head>
<body class="bg-stone-50 text-stone-800">
<main class="container mx-auto px-4 py-8 md:py-12">
<header class="text-center mb-12">
<h1 class="text-4xl md:text-5xl font-bold text-teal-700 mb-2">Math is Beautiful</h1>
<p class="text-lg text-stone-600 max-w-3xl mx-auto">An interactive exploration of the work of mathematical artist <span class="font-semibold text-teal-600">Hamid Naderi Yeganeh</span>.</p>
</header>
<section id="artist" class="mb-16 bg-white p-8 rounded-xl shadow-sm border border-stone-200">
<h2 class="text-3xl font-bold text-teal-700 mb-4 text-center">The Artist and The Process</h2>
<div class="grid md:grid-cols-2 gap-8 items-center">
<div class="text-stone-700 leading-relaxed">
<p class="mb-4">Hamid Naderi Yeganeh, an Iranian mathematician and artist, discovered that he could find "interesting shapes that resemble real things" by using mathematical formulas. By writing computer programs, he can generate thousands of unique, intricate images based on a single formula, simply by changing a few parameters.</p>
<p class="mb-4">His process is one of exploration. He often doesn't know what an equation will produce until he runs the program. Finding recognizable objects, like a boat or animals, can be a happy accident after weeks of tweaking formulas. He shares his formulas openly to demonstrate the creative power and beauty inherent in mathematics.</p>
</div>
<div class="text-center text-5xl md:text-8xl text-teal-100 font-bold select-none">
∫ f(x) dx
</div>
</div>
</section>
<section id="gallery" class="mb-16">
<div class="text-center mb-8">
<h2 class="text-3xl font-bold text-teal-700 mb-2">The Gallery & The Math</h2>
<p class="text-stone-600 max-w-3xl mx-auto">Select an artwork to see how it's made. Use the sliders to manipulate the mathematical formula in real time and see how the image changes. This section reveals the direct connection between numbers and intricate beauty.</p>
</div>
<div class="flex justify-center flex-wrap gap-3 mb-6" id="artwork-selector">
<button data-art="bird" class="art-btn px-4 py-2 bg-white border border-stone-300 rounded-lg shadow-sm hover:bg-teal-50 transition-colors">A Bird in Flight</button>
<button data-art="boat" class="art-btn px-4 py-2 bg-white border border-stone-300 rounded-lg shadow-sm hover:bg-teal-50 transition-colors">Boat</button>
<button data-art="circles" class="art-btn px-4 py-2 bg-white border border-stone-300 rounded-lg shadow-sm hover:bg-teal-50 transition-colors">Cosmic Circles</button>
</div>
<div class="bg-white p-4 sm:p-6 rounded-xl shadow-lg border border-stone-200">
<div class="chart-container mx-auto bg-stone-100 rounded-lg shadow-inner">
<canvas id="main-canvas"></canvas>
</div>
<div id="controls" class="mt-6 grid sm:grid-cols-2 md:grid-cols-3 gap-6">
</div>
</div>
<div id="formula-display" class="mt-4 text-center text-stone-600 bg-stone-100 p-4 rounded-lg">Select an artwork to see its formula.</div>
</section>
<section id="creator">
<div class="text-center mb-8">
<h2 class="text-3xl font-bold text-teal-700 mb-2">Create Your Own</h2>
<p class="text-stone-600 max-w-3xl mx-auto">Now it's your turn. Use the editor below to write your own drawing logic using JavaScript. The code runs in a loop from k=0 to a specified count. You can use the parameters `p1` to `p4` from the sliders. Press 'Generate' to see your creation.</p>
</div>
<div class="bg-white p-6 rounded-xl shadow-lg border border-stone-200 grid lg:grid-cols-2 gap-6">
<div>
<div class="chart-container mx-auto bg-stone-100 rounded-lg shadow-inner">
<canvas id="creator-canvas"></canvas>
</div>
</div>
<div class="flex flex-col">
<div class="flex-grow mb-4">
<textarea id="code-editor" class="w-full h-48 p-3 font-mono text-sm bg-stone-900 text-green-400 rounded-lg border border-stone-700 focus:ring-2 focus:ring-teal-500 focus:outline-none resize-none"></textarea>
</div>
<div id="creator-controls" class="grid grid-cols-2 gap-4 mb-4">
</div>
<button id="generate-btn" class="w-full bg-teal-600 text-white font-bold py-3 px-4 rounded-lg hover:bg-teal-700 transition-colors">Generate Artwork</button>
</div>
</div>
</section>
</main>
<footer class="text-center py-6 bg-stone-100 border-t border-stone-200 mt-12">
<p class="text-stone-500">Inspired by the work of Hamid Naderi Yeganeh. Application created for educational purposes.</p>
</footer>
<script>
const state = {
currentArt: 'bird',
params: {},
artFunctions: {},
controlConfig: {}
};
const setupControl = (key, config) => {
const container = document.createElement('div');
const label = document.createElement('label');
label.htmlFor = key;
label.className = "block text-sm font-medium text-stone-700 mb-1";
label.textContent = `${config.label}: `;
const valueSpan = document.createElement('span');
valueSpan.id = `${key}-value`;
valueSpan.className = "font-bold text-teal-600";
valueSpan.textContent = config.value;
label.appendChild(valueSpan);
const slider = document.createElement('input');
slider.type = 'range';
slider.id = key;
slider.min = config.min;
slider.max = config.max;
slider.step = config.step;
slider.value = config.value;
slider.dataset.art = state.currentArt;
container.appendChild(label);
container.appendChild(slider);
slider.addEventListener('input', (e) => {
state.params[key] = parseFloat(e.target.value);
valueSpan.textContent = e.target.value;
if (slider.dataset.art === state.currentArt) {
requestAnimationFrame(() => state.artFunctions[state.currentArt]());
}
});
return container;
};
const setupControlsForArt = (artKey) => {
const controlsContainer = document.getElementById('controls');
controlsContainer.innerHTML = '';
state.params = {};
const config = state.controlConfig[artKey];
for (const key in config) {
state.params[key] = config[key].value;
const controlEl = setupControl(key, config[key]);
controlsContainer.appendChild(controlEl);
}
};
const updateFormulaDisplay = (artKey) => {
const display = document.getElementById('formula-display');
const formulas = {
bird: "x(t) = cos(t) * (e^cos(t) - 2cos(4t) - sin(t/12)^5), y(t) = sin(t) * (e^cos(t) - 2cos(4t) - sin(t/12)^5). Lines are drawn between consecutive points.",
boat: "For k=1..N, draw line from (sin(πk/N)² , cos(πk/(N/2))) to (sin(πk/(N/2)) , cos(πk/N)²). A simple formula creating surprising complexity.",
circles: "For k=1..N, draw circle at x = cos(Aπk/N) + P*cos(Bπk/N), y = sin(Cπk/N) + Q*sin(Dπk/N). A dance of trigonometric functions."
};
display.textContent = formulas[artKey] || "Select an artwork to see its formula.";
}
const switchArt = (artKey) => {
state.currentArt = artKey;
document.querySelectorAll('.art-btn').forEach(btn => {
if(btn.dataset.art === artKey) {
btn.classList.add('bg-teal-600', 'text-white', 'border-teal-600');
btn.classList.remove('bg-white', 'border-stone-300');
} else {
btn.classList.remove('bg-teal-600', 'text-white', 'border-teal-600');
btn.classList.add('bg-white', 'border-stone-300');
}
});
setupControlsForArt(artKey);
updateFormulaDisplay(artKey);
requestAnimationFrame(() => state.artFunctions[artKey]());
};
window.addEventListener('load', () => {
const canvas = document.getElementById('main-canvas');
const ctx = canvas.getContext('2d');
const resizeCanvas = () => {
const container = canvas.parentElement;
const dpr = window.devicePixelRatio || 1;
canvas.width = container.clientWidth * dpr;
canvas.height = container.clientHeight * dpr;
ctx.scale(dpr, dpr);
if (state.artFunctions[state.currentArt]) {
state.artFunctions[state.currentArt]();
}
};
state.controlConfig = {
bird: {
points: { label: 'Points', min: 1000, max: 10000, step: 100, value: 5000 },
zoom: { label: 'Zoom', min: 20, max: 150, step: 1, value: 80 },
paramA: { label: 'A (exp)', min: 0.5, max: 1.5, step: 0.01, value: 1 },
paramB: { label: 'B (cos)', min: 1, max: 8, step: 0.1, value: 4 },
paramC: { label: 'C (sin)', min: 2, max: 24, step: 1, value: 12 },
},
boat: {
lines: { label: 'Lines', min: 100, max: 4000, step: 50, value: 2000 },
scale: { label: 'Scale', min: 50, max: 300, step: 1, value: 150 },
skew: { label: 'Skew', min: 0.1, max: 2, step: 0.05, value: 1 },
wave: { label: 'Wave', min: 0.5, max: 4, step: 0.1, value: 2 },
},
circles: {
count: { label: 'Circles', min: 500, max: 6000, step: 100, value: 3000 },
radius: { label: 'Radius', min: 0.1, max: 5, step: 0.1, value: 1 },
pA: { label: 'Freq A', min: 0, max: 20, step: 0.1, value: 4 },
pB: { label: 'Freq B', min: 0, max: 20, step: 0.1, value: 8 },
pC: { label: 'Freq C', min: 0, max: 20, step: 0.1, value: 6 },
pD: { label: 'Freq D', min: 0, max: 20, step: 0.1, value: 12 },
}
};
state.artFunctions.bird = () => {
const w = canvas.width / (window.devicePixelRatio || 1);
const h = canvas.height / (window.devicePixelRatio || 1);
ctx.clearRect(0, 0, w, h);
ctx.strokeStyle = '#0d9488';
ctx.lineWidth = 1;
ctx.beginPath();
const N = state.params.points;
const zoom = state.params.zoom;
const A = state.params.paramA;
const B = state.params.paramB;
const C = state.params.paramC;
let lastX, lastY;
for (let k = 0; k < N; k++) {
const t = 24 * Math.PI * k / N;
const r = A * Math.exp(Math.cos(t)) - 2 * Math.cos(B * t) - Math.pow(Math.sin(t / C), 5);
const x = w / 2 + zoom * r * Math.cos(t);
const y = h / 2 + zoom * r * Math.sin(t);
if (k === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.stroke();
};
state.artFunctions.boat = () => {
const w = canvas.width / (window.devicePixelRatio || 1);
const h = canvas.height / (window.devicePixelRatio || 1);
ctx.clearRect(0, 0, w, h);
ctx.strokeStyle = 'rgba(13, 148, 136, 0.7)';
ctx.lineWidth = 0.5;
ctx.beginPath();
const N = state.params.lines;
const scale = state.params.scale;
const skew = state.params.skew;
const wave = state.params.wave;
for (let k = 1; k <= N; k++) {
const t1 = Math.PI * k / N;
const t2 = Math.PI * k / (N / wave);
const x1 = w / 2 + scale * (Math.pow(Math.sin(t1), 2) - 0.5);
const y1 = h / 2 + scale * (Math.cos(t2) - 0.5) * skew;
const x2 = w / 2 + scale * (Math.sin(t2) - 0.5);
const y2 = h / 2 + scale * (Math.pow(Math.cos(t1), 2) - 0.5) * skew;
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
}
ctx.stroke();
};
state.artFunctions.circles = () => {
const w = canvas.width / (window.devicePixelRatio || 1);
const h = canvas.height / (window.devicePixelRatio || 1);
ctx.clearRect(0, 0, w, h);
ctx.fillStyle = '#0d9488';
const N = state.params.count;
const R = state.params.radius;
const A = state.params.pA;
const B = state.params.pB;
const C = state.params.pC;
const D = state.params.pD;
const scale = Math.min(w, h) / 4;
for (let k = 1; k <= N; k++) {
const t = 2 * Math.PI * k / N;
const x = w / 2 + scale * (Math.cos(A * t) + Math.cos(B * t) / 2);
const y = h / 2 + scale * (Math.sin(C * t) - Math.sin(D * t) / 2);
ctx.beginPath();
ctx.arc(x, y, R, 0, 2 * Math.PI);
ctx.fill();
}
};
document.getElementById('artwork-selector').addEventListener('click', (e) => {
if (e.target.tagName === 'BUTTON') {
switchArt(e.target.dataset.art);
}
});
new ResizeObserver(resizeCanvas).observe(canvas.parentElement);
switchArt('bird');
resizeCanvas();
const creatorCanvas = document.getElementById('creator-canvas');
const creatorCtx = creatorCanvas.getContext('2d');
let creatorParams = {};
const resizeCreatorCanvas = () => {
const container = creatorCanvas.parentElement;
const dpr = window.devicePixelRatio || 1;
creatorCanvas.width = container.clientWidth * dpr;
creatorCanvas.height = container.clientHeight * dpr;
creatorCtx.scale(dpr, dpr);
};
new ResizeObserver(resizeCreatorCanvas).observe(creatorCanvas.parentElement);
const creatorControlsContainer = document.getElementById('creator-controls');
const creatorConfig = {
count: { label: 'Count', min: 100, max: 10000, step: 100, value: 2000 },
p1: { label: 'Param 1', min: -5, max: 5, step: 0.1, value: 1 },
p2: { label: 'Param 2', min: -5, max: 5, step: 0.1, value: 1 },
p3: { label: 'Param 3', min: -5, max: 5, step: 0.1, value: 1 },
};
const setupCreatorControl = (key, config) => {
const container = document.createElement('div');
const label = document.createElement('label');
label.htmlFor = `creator-${key}`;
label.className = "block text-sm font-medium text-stone-700 mb-1";
label.textContent = `${config.label}: `;
const valueSpan = document.createElement('span');
valueSpan.className = "font-bold text-teal-600";
valueSpan.textContent = config.value;
label.appendChild(valueSpan);
const slider = document.createElement('input');
slider.type = 'range';
slider.id = `creator-${key}`;
slider.min = config.min;
slider.max = config.max;
slider.step = config.step;
slider.value = config.value;
container.appendChild(label);
container.appendChild(slider);
slider.addEventListener('input', (e) => {
creatorParams[key] = parseFloat(e.target.value);
valueSpan.textContent = e.target.value;
});
return container;
};
for (const key in creatorConfig) {
creatorParams[key] = creatorConfig[key].value;
const controlEl = setupCreatorControl(key, creatorConfig[key]);
creatorControlsContainer.appendChild(controlEl);
}
const codeEditor = document.getElementById('code-editor');
codeEditor.value = `// Welcome! 'k' is the loop variable from 0 to 'count'.
// 'w' and 'h' are canvas width and height.
// Use ctx.moveTo(x,y) and ctx.lineTo(x,y) to draw.
// Example: A simple spirograph
const t = 2 * Math.PI * k / count;
const R = w / 3;
const r = R / p1;
const d = r * p2;
const x = (R - r) * Math.cos(t) + d * Math.cos((R - r) / r * t);
const y = (R - r) * Math.sin(t) - d * Math.sin((R - r) / r * t);
if (k === 0) {
ctx.moveTo(w/2 + x, h/2 + y);
} else {
ctx.lineTo(w/2 + x, h/2 + y);
}`;
document.getElementById('generate-btn').addEventListener('click', () => {
const userCode = codeEditor.value;
const w = creatorCanvas.width / (window.devicePixelRatio || 1);
const h = creatorCanvas.height / (window.devicePixelRatio || 1);
creatorCtx.clearRect(0, 0, w, h);
creatorCtx.strokeStyle = '#0d9488';
creatorCtx.lineWidth = 1;
creatorCtx.beginPath();
try {
const drawLogic = new Function('ctx', 'k', 'count', 'w', 'h', 'p1', 'p2', 'p3', userCode);
const count = creatorParams.count;
for (let k = 0; k < count; k++) {
drawLogic(creatorCtx, k, count, w, h, creatorParams.p1, creatorParams.p2, creatorParams.p3);
}
creatorCtx.stroke();
} catch (e) {
console.error("Error in user code:", e);
creatorCtx.font = "16px Arial";
creatorCtx.fillStyle = "red";
creatorCtx.textAlign = "center";
creatorCtx.fillText("Error in code. Check console.", w / 2, h / 2);
}
});
resizeCreatorCanvas();
document.getElementById('generate-btn').click();
});
</script>
</body>
</html>