// 037 - DNA Double Helix (anatomy)
// 1:1 Original algorithm engine source
function createDNADoubleHelix() {
const BASE_PAIRS = 40;
return {
setup() {
},
render(context, timeState, params) {
const { ctx, width, height } = context;
const speed = Number(params.rotationSpeed || 1);
const helixRadius = Number(params.helixRadius || 85);
const t = timeState.time * speed;
ctx.fillStyle = "#05060a";
ctx.fillRect(0, 0, width, height);
const cx = width * 0.5;
const cy = height * 0.5;
const helixHeight = height * 0.85;
const startY = cy - helixHeight * 0.5;
const nodes = [];
for (let i = 0; i < BASE_PAIRS; i++) {
const normY = i / (BASE_PAIRS - 1);
const y = startY + normY * helixHeight;
const theta = normY * Math.PI * 4 + t * 2;
const x1 = Math.cos(theta) * helixRadius;
const z1 = Math.sin(theta) * helixRadius;
const x2 = Math.cos(theta + Math.PI) * helixRadius;
const z2 = Math.sin(theta + Math.PI) * helixRadius;
nodes.push({ y, x1, z1, x2, z2, pairType: i % 4 });
}
nodes.sort((a, b) => (a.z1 + a.z2) / 2 - (b.z1 + b.z2) / 2);
for (let i = 0; i < nodes.length; i++) {
const n = nodes[i];
const px1 = cx + n.x1;
const py1 = n.y;
const depth1 = (n.z1 + helixRadius) / (helixRadius * 2);
const px2 = cx + n.x2;
const py2 = n.y;
const depth2 = (n.z2 + helixRadius) / (helixRadius * 2);
ctx.beginPath();
ctx.moveTo(px1, py1);
ctx.lineTo(px2, py2);
const baseHue = n.pairType < 2 ? 190 : 340;
const avgDepth = (depth1 + depth2) * 0.5;
ctx.strokeStyle = hsla(baseHue, 85, 60, 0.4 + avgDepth * 0.5);
ctx.lineWidth = 1.8 + avgDepth * 1.5;
ctx.stroke();
ctx.fillStyle = hsla(185, 95, 70, 0.6 + depth1 * 0.4);
ctx.beginPath();
ctx.arc(px1, py1, 3.5 + depth1 * 3.5, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = hsla(280, 95, 75, 0.6 + depth2 * 0.4);
ctx.beginPath();
ctx.arc(px2, py2, 3.5 + depth2 * 3.5, 0, Math.PI * 2);
ctx.fill();
}
}
};
}
// Default parameters from content metadata
const defaultParams = [
{
"key": "rotationSpeed",
"label": "Axial Spin Speed",
"type": "range",
"min": 0.2,
"max": 3,
"step": 0.1,
"defaultValue": 1,
"description": "Molecular helical revolution rate"
},
{
"key": "helixRadius",
"label": "Strand Major Radius",
"type": "range",
"min": 40,
"max": 130,
"step": 5,
"defaultValue": 85,
"description": "Sugar-phosphate backbone width"
}
];
if (!window.__art_instances) window.__art_instances = {};
if (!window.__art_instances['dna-double-helix']) {
const inst = typeof createDNADoubleHelix === 'function' ? createDNADoubleHelix() : null;
if (inst && inst.setup) {
inst.setup({ ctx, width, height, dpr: 1, aspectRatio: width / height }, defaultParams);
}
window.__art_instances['dna-double-helix'] = inst;
}
const instance = window.__art_instances['dna-double-helix'];
if (instance && instance.render) {
instance.render(
{ ctx, width, height, dpr: 1, aspectRatio: width / height },
{ time, deltaTime: dt, frameCount: Math.floor(time * 60), fps: 60 },
defaultParams
);
}