Skip to content
Kiln0.10Get Kiln

Faceted gold sails on four trussed spars

Earlier examples from earlier Kiln versions.

Historical gallery render of Solar sail courier; see the poster provenance below.

Drag to orbit after opening. The model starts stationary.

For your engine. The download is a standard glTF 2.0 binary (GLB) with PBR metallic-roughness materials. Optional extensions declared by this file: KHR_materials_emissive_strength. Importer support varies; see the Blender and Unity guide. glTF stores materials, not lighting or tone mapping, so your engine decides how they read. This 3D view tone-maps with Review Neutral, the Khronos PBR Neutral construction with a smaller glare offset (0.015 instead of 0.04), at exposure 0.9. Its Tone mapping control also shows ACES and Linear, for comparison.

Build measurements

Triangles
27,756
Estimated draws
627
Materials
9
Textures
0
Animation clips
1
Bounds X × Y × Z
3.12 × 5.25 × 5.25 m
Build warnings
0

Measurements come from this build.

Download this build

Runtime: 345,640 bytes. Original: 345,404 bytes.

These GLBs are build outputs of the MIT-licensed example source; no separate terms are stated for the builds.

Runtime provenance metadata
SHA-256 download hashes
Runtime GLB
6de0c5d5394db09abe94dc9fb857ddeed9e0e4a4fe89e7be75600e6f287c5c46
Original GLB
839a3ac2181c6ec5ed4ec1709bd1bd18131903539b3746128b6ecb8bd3f05388
Source
0a3aac41c77094e96aaeed3414802b20fb3143398715cd8ffcc008d626821164

The example source is part of the Kiln repository. Repository licence: MIT.

These GLBs are build outputs of the MIT-licensed example source; no separate terms are stated for the builds.

Recorded authorship

Gemini 3.8 Flash through Antigravity CLI (agy) ·

Recorded Antigravity / gemini-3.8-flash-high run; configured model, no provider-internal attestation.

Source access
Fresh project-local authoring workspace with a brief, installed skills and Kiln tools.
Inherited context
Fresh independent authoring conversation.
Starting example
None supplied.
Human input
Maintainer supplied the brief and reviewed actual exports and uniform gallery posters. Final source bytes are preserved from the model export.
Authoring review
Actual GPU images inspected independently. Stylized folded sail geometry; physical sail deployment was not verified.

Gallery GPU render of this exact source. The separate poster receipt records image, camera, renderer, and the artifact hash of the GLB bytes the image was rendered from.

Poster camera and render record

The source behind this build

solar-sail-courier.kiln.js
const meta = {
  name: 'Solar Sail Courier',
  category: 'vehicle',
  role: 'vehicle'
};

function build() {
  const root = createRoot('SolarSailCourier');

  // PBR Materials
  const hullIvory = gameMaterial(0xf6f4ec, { roughness: 0.32, metalness: 0.10 });
  const hullIvoryDark = gameMaterial(0xdcd6c8, { roughness: 0.42, metalness: 0.15 });
  const darkTitanium = gameMaterial(0x20242b, { roughness: 0.35, metalness: 0.88 });
  const sparTruss = gameMaterial(0x323842, { roughness: 0.38, metalness: 0.82 });
  const goldHardware = gameMaterial(0xdfba4e, { roughness: 0.24, metalness: 0.92 });
  const glowCyan = gameMaterial(0x00d4ff, { emissive: 0x00b4d8, emissiveIntensity: 2.5, roughness: 0.20 });
  const engineMetal = gameMaterial(0x16181d, { roughness: 0.28, metalness: 0.92 });

  // Brilliant reflective origami solar foil membranes
  const foilGold = gameMaterial(0xf5be38, { roughness: 0.14, metalness: 0.96, flatShading: true });
  const foilCopper = gameMaterial(0xd46632, { roughness: 0.16, metalness: 0.94, flatShading: true });
  foilGold.side = THREE.DoubleSide;
  foilCopper.side = THREE.DoubleSide;

  // =========================================================================
  // 1. COMPACT IVORY CARGO CORE (+X is forward, -X is aft)
  // =========================================================================
  const coreRoot = createRoot('CargoCore');
  root.add(coreRoot);

  // Main fuselage cylindrical body
  createPart('MainFuselage', cylinderGeo(0.28, 0.28, 0.90, 24), hullIvory, {
    position: [0.05, 0, 0],
    rotation: [0, 0, 90],
    parent: coreRoot
  });

  // Forward aerodynamic tapered nose cone
  createPart('NoseCone', cylinderGeo(0.08, 0.28, 0.80, 24), hullIvory, {
    position: [0.90, 0, 0],
    rotation: [0, 0, 90],
    parent: coreRoot
  });

  // Rounded nose cap
  createPart('NoseCap', sphereGeo(0.08, 16, 12), hullIvory, {
    position: [1.30, 0, 0],
    parent: coreRoot
  });

  // Forward telemetry sensor spike
  createPart('SensorSpike', cylinderGeo(0.008, 0.018, 0.40, 8), darkTitanium, {
    position: [1.50, 0, 0],
    rotation: [0, 0, 90],
    parent: coreRoot
  });

  // Optical sensor dome / telemetry blister
  createPart('SensorDomeTop', sphereGeo(0.06, 16, 12), glowCyan, {
    position: [1.10, 0.22, 0],
    parent: coreRoot
  });
  createPart('SensorHousingTop', cylinderGeo(0.065, 0.075, 0.05, 16), darkTitanium, {
    position: [1.10, 0.19, 0],
    parent: coreRoot
  });

  // High-gain communications dish assembly
  const commsPivot = createPivot('CommsDish', [0.45, 0.32, 0], coreRoot);
  commsPivot.rotation.z = (25 * Math.PI) / 180;
  createPart('CommsStrut', cylinderGeo(0.014, 0.014, 0.12, 8), darkTitanium, {
    position: [0, -0.06, 0],
    parent: commsPivot
  });
  createPart('CommsDishReflector', coneGeo(0.18, 0.06, 20), hullIvory, {
    position: [0, 0.02, 0],
    rotation: [180, 0, 0],
    parent: commsPivot
  });
  createPart('CommsFeedHorn', cylinderGeo(0.005, 0.010, 0.09, 8), goldHardware, {
    position: [0, 0.06, 0],
    parent: commsPivot
  });
  createPart('CommsFeedTip', sphereGeo(0.014, 8, 8), glowCyan, {
    position: [0, 0.11, 0],
    parent: commsPivot
  });

  // 4 Aft ceramic thermal radiator fins
  const finAngles = [45, 135, 225, 315];
  for (let f = 0; f < 4; f++) {
    const fDeg = finAngles[f];
    const fRad = (fDeg * Math.PI) / 180;
    const fy = 0.31 * Math.cos(fRad);
    const fz = 0.31 * Math.sin(fRad);
    createPart('RadiatorFin_' + f, boxGeo(0.40, 0.15, 0.012), hullIvory, {
      position: [-0.68, fy, fz],
      rotation: [fDeg, 0, 0],
      parent: coreRoot
    });
    createPart('RadiatorFinTrim_' + f, boxGeo(0.42, 0.018, 0.02), darkTitanium, {
      position: [-0.68, fy * 1.22, fz * 1.22],
      rotation: [fDeg, 0, 0],
      parent: coreRoot
    });
  }

  // Starboard androgynous docking collar assembly
  const dockPivot = createPivot('DockingPort', [0.15, 0, 0.30], coreRoot);
  dockPivot.rotation.y = (90 * Math.PI) / 180;
  createPart('DockingRing', cylinderGeo(0.13, 0.13, 0.06, 18), darkTitanium, {
    position: [0, 0, 0],
    rotation: [0, 0, 90],
    parent: dockPivot
  });
  createPart('DockingHatch', cylinderGeo(0.10, 0.10, 0.02, 18), hullIvoryDark, {
    position: [0.03, 0, 0],
    rotation: [0, 0, 90],
    parent: dockPivot
  });
  // 3 Guide petals
  for (let p = 0; p < 3; p++) {
    const pRad = (p * 120 * Math.PI) / 180;
    createPart('DockPetal_' + p, boxGeo(0.04, 0.03, 0.01), goldHardware, {
      position: [0.03, 0.11 * Math.cos(pRad), 0.11 * Math.sin(pRad)],
      rotation: [0, 0, 0],
      parent: dockPivot
    });
  }
  createPart('DockStatusLight', sphereGeo(0.012, 8, 8), glowCyan, {
    position: [0.03, 0.13, 0],
    parent: dockPivot
  });

  // Aft hull taper transition
  createPart('AftTransition', cylinderGeo(0.28, 0.20, 0.50, 24), hullIvory, {
    position: [-0.65, 0, 0],
    rotation: [0, 0, 90],
    parent: coreRoot
  });

  // Aft structural engine collar
  createPart('EngineCollar', cylinderGeo(0.21, 0.21, 0.16, 24), darkTitanium, {
    position: [-0.98, 0, 0],
    rotation: [0, 0, 90],
    parent: coreRoot
  });

  // Ion thruster expansion nozzle
  createPart('IonNozzle', cylinderGeo(0.14, 0.26, 0.36, 24), engineMetal, {
    position: [-1.24, 0, 0],
    rotation: [0, 0, 90],
    parent: coreRoot
  });

  // Ion propulsion luminous discharge grid
  createPart('IonGrid', cylinderGeo(0.135, 0.135, 0.04, 20), glowCyan, {
    position: [-1.12, 0, 0],
    rotation: [0, 0, 90],
    parent: coreRoot
  });

  // 4 modular cylindrical cargo pods strapped around the fuselage
  const cargoOffsets = [
    [0.05, 0.25, 0.25],
    [0.05, -0.25, 0.25],
    [0.05, -0.25, -0.25],
    [0.05, 0.25, -0.25]
  ];
  for (let c = 0; c < 4; c++) {
    const [cx, cy, cz] = cargoOffsets[c];
    createPart('CargoPod_' + c, cylinderGeo(0.08, 0.08, 0.65, 16), hullIvoryDark, {
      position: [cx, cy, cz],
      rotation: [0, 0, 90],
      parent: coreRoot
    });
    // Pod endcaps
    createPart('CargoCapFwd_' + c, sphereGeo(0.08, 12, 8), darkTitanium, {
      position: [cx + 0.325, cy, cz],
      parent: coreRoot
    });
    createPart('CargoCapAft_' + c, sphereGeo(0.08, 12, 8), darkTitanium, {
      position: [cx - 0.325, cy, cz],
      parent: coreRoot
    });
    // Gold restraint straps
    createPart('CargoStrapA_' + c, torusGeo(0.085, 0.01, 8, 16), goldHardware, {
      position: [cx + 0.18, cy, cz],
      rotation: [0, 90, 0],
      parent: coreRoot
    });
    createPart('CargoStrapB_' + c, torusGeo(0.085, 0.01, 8, 16), goldHardware, {
      position: [cx - 0.18, cy, cz],
      rotation: [0, 90, 0],
      parent: coreRoot
    });
  }

  // 4 Attitude Control Thruster (RCS) quads
  const rcsAngles = [0, 90, 180, 270];
  for (let r = 0; r < 4; r++) {
    const deg = rcsAngles[r];
    const rad = (deg * Math.PI) / 180;
    const ry = 0.27 * Math.cos(rad);
    const rz = 0.27 * Math.sin(rad);
    // Forward RCS
    createPart('RCSBlockFwd_' + r, boxGeo(0.06, 0.05, 0.05), darkTitanium, {
      position: [0.75, ry, rz],
      rotation: [deg, 0, 0],
      parent: coreRoot
    });
    createPart('RCSNozzleFwd_' + r, coneGeo(0.02, 0.04, 8), goldHardware, {
      position: [0.75, ry * 1.15, rz * 1.15],
      rotation: [deg, 0, 0],
      parent: coreRoot
    });
  }

  // Central octagonal structural collar ring at X=0 where the 4 spars attach
  createPart('CollarRing', cylinderGeo(0.33, 0.33, 0.14, 16), darkTitanium, {
    position: [0, 0, 0],
    rotation: [0, 0, 90],
    parent: coreRoot
  });
  createPart('CollarFlangeFwd', torusGeo(0.33, 0.016, 8, 24), goldHardware, {
    position: [0.07, 0, 0],
    rotation: [0, 90, 0],
    parent: coreRoot
  });
  createPart('CollarFlangeAft', torusGeo(0.33, 0.016, 8, 24), goldHardware, {
    position: [-0.07, 0, 0],
    rotation: [0, 90, 0],
    parent: coreRoot
  });

  // =========================================================================
  // 2. ARTICULATED DELICATE SPARS & ORIGAMI SOLAR FOIL WINGS
  // =========================================================================
  const quadAnglesDeg = [45, 135, 225, 315];

  for (let k = 0; k < 4; k++) {
    const qDeg = quadAnglesDeg[k];

    // Orientation root for quadrant k
    // Local frame: +X forward, +Y radially along boom, +Z transverse along sail
    const mountPivot = createPivot('Mount_' + k, [0, 0, 0], root);
    mountPivot.rotation.x = (qDeg * Math.PI) / 180;

    // Collar hinge clevis bracket
    createPart('CollarClevis_' + k, boxGeo(0.12, 0.06, 0.08), darkTitanium, {
      position: [0, 0.33, 0],
      parent: mountPivot
    });

    // PRIMARY ARTICULATED BOOM JOINT
    // Rotates around local Z axis for stowed/deployed transition
    const boomJoint = createPivot('Boom_' + k, [0, 0.36, 0], mountPivot);

    // Boom root hinge pin
    createPart('HingePinRoot_' + k, cylinderGeo(0.016, 0.016, 0.10, 12), goldHardware, {
      position: [0, 0, 0],
      rotation: [90, 0, 0],
      parent: boomJoint
    });

    // Hydraulic deployment actuator pushrod
    beamBetween('ActuatorCylinder_' + k, [0.08, -0.05, 0], [0.03, 0.22, 0], 0.014, darkTitanium, {
      parent: boomJoint
    });
    beamBetween('ActuatorPiston_' + k, [0.03, 0.22, 0], [0.01, 0.35, 0], 0.008, goldHardware, {
      parent: boomJoint
    });

    // --- INNER LATTICE SPAR (Length = 1.35m along +Y) ---
    buildLatticeSparSegment('InnerSpar_' + k, 0.05, 1.35, 0.075, sparTruss, goldHardware, boomJoint);

    // Mechanical hinge brackets along inner spar for sail panel attachment
    const innerHingeY = [0.25, 0.55, 0.85, 1.15];
    for (let h = 0; h < innerHingeY.length; h++) {
      const hy = innerHingeY[h];
      createPart('InnerHingeLug_' + k + '_' + h, boxGeo(0.03, 0.04, 0.04), darkTitanium, {
        position: [0, hy, 0.045],
        parent: boomJoint
      });
      createPart('InnerHingePin_' + k + '_' + h, cylinderGeo(0.008, 0.008, 0.05, 10), goldHardware, {
        position: [0, hy, 0.065],
        rotation: [0, 0, 90],
        parent: boomJoint
      });
    }

    // --- MID-SPAR ARTICULATED JOINT ---
    const midBoomJoint = createPivot('MidBoom_' + k, [0, 1.35, 0], boomJoint);

    // Mid-spar hinge knuckle & torsion spring
    createPart('MidHingeBarrel_' + k, cylinderGeo(0.02, 0.02, 0.08, 12), darkTitanium, {
      position: [0, 0, 0],
      rotation: [90, 0, 0],
      parent: midBoomJoint
    });
    createPart('MidHingePin_' + k, cylinderGeo(0.01, 0.01, 0.10, 12), goldHardware, {
      position: [0, 0, 0],
      rotation: [90, 0, 0],
      parent: midBoomJoint
    });
    createPart('TorsionSpring_' + k, torusGeo(0.024, 0.006, 8, 16), goldHardware, {
      position: [0, 0, 0.03],
      parent: midBoomJoint
    });
    // Hydraulic mid-hinge actuator pushrod
    beamBetween('MidActuator_' + k, [-0.03, 1.28, 0.03], [-0.02, 1.35, 0.03], 0.007, darkTitanium, { parent: boomJoint });
    beamBetween('MidPiston_' + k, [-0.02, 1.35, 0.03], [-0.01, 1.42, 0.03], 0.004, goldHardware, { parent: boomJoint });

    // --- OUTER LATTICE SPAR (Length = 1.35m along +Y) ---
    buildLatticeSparSegment('OuterSpar_' + k, 0.05, 1.35, 0.060, sparTruss, goldHardware, midBoomJoint);

    // Mechanical hinge brackets along outer spar
    const outerHingeY = [0.25, 0.55, 0.85, 1.15];
    for (let h = 0; h < outerHingeY.length; h++) {
      const hy = outerHingeY[h];
      createPart('OuterHingeLug_' + k + '_' + h, boxGeo(0.025, 0.035, 0.035), darkTitanium, {
        position: [0, hy, 0.040],
        parent: midBoomJoint
      });
      createPart('OuterHingePin_' + k + '_' + h, cylinderGeo(0.007, 0.007, 0.045, 10), goldHardware, {
        position: [0, hy, 0.055],
        rotation: [0, 0, 90],
        parent: midBoomJoint
      });
    }

    // Outer boom tip package: probe, optical beacon, tension rigging spreader
    createPart('TipHub_' + k, sphereGeo(0.04, 12, 8), darkTitanium, {
      position: [0, 1.38, 0],
      parent: midBoomJoint
    });
    createPart('TipBeacon_' + k, cylinderGeo(0.015, 0.015, 0.05, 12), glowCyan, {
      position: [0, 1.42, 0],
      parent: midBoomJoint
    });
    createPart('TipProbe_' + k, cylinderGeo(0.004, 0.008, 0.18, 8), goldHardware, {
      position: [0, 1.50, 0],
      parent: midBoomJoint
    });

    // Tension rigging spreader spar
    beamBetween('TipSpreaderA_' + k, [0, 1.38, 0], [0.06, 1.32, 0.14], 0.007, sparTruss, { parent: midBoomJoint });
    beamBetween('TipSpreaderB_' + k, [0, 1.38, 0], [-0.06, 1.32, 0.14], 0.007, sparTruss, { parent: midBoomJoint });

    // --- ORIGAMI SOLAR FOIL WINGS ---
    // Inner Segmented Origami Sail Panel (hinged on inner boom)
    const sailInnerJoint = createPivot('SailInner_' + k, [0, 0.15, 0.07], boomJoint);
    createOrigamiSailPanel({
      name: 'InnerFoil_' + k,
      rMin: 0.12,
      rMax: 1.34,
      angleRad: 1.25,
      numRings: 12,
      numRays: 10,
      foldDepth: 0.058,
      matGold: foilGold,
      matCopper: foilCopper,
      parent: sailInnerJoint
    });
    // Inner panel spar-side reinforcement batten
    beamBetween('InnerBatten_' + k, [0, 0.12, 0], [0, 1.34, 0], 0.008, darkTitanium, {
      parent: sailInnerJoint
    });

    // Outer Segmented Origami Sail Panel (hinged on outer boom)
    const sailOuterJoint = createPivot('SailOuter_' + k, [0, 0.02, 0.06], midBoomJoint);
    createOrigamiSailPanel({
      name: 'OuterFoil_' + k,
      rMin: 0.02,
      rMax: 1.36,
      angleRad: 1.25,
      numRings: 14,
      numRays: 12,
      foldDepth: 0.068,
      matGold: foilGold,
      matCopper: foilCopper,
      parent: sailOuterJoint
    });
    // Outer panel spar-side reinforcement batten
    beamBetween('OuterBatten_' + k, [0, 0.02, 0], [0, 1.36, 0], 0.007, darkTitanium, {
      parent: sailOuterJoint
    });

    // Tension rigging cable between inner and outer sail battens
    beamBetween('RiggingCable_' + k, [0, 1.34, 0], [0, 0.02, 0], 0.004, goldHardware, {
      parent: sailInnerJoint
    });

    // Outer tension guy-wire from tip spreader to sail outer tip
    const outerTipY = 1.36 * Math.cos(1.25);
    const outerTipZ = 1.36 * Math.sin(1.25);
    beamBetween('TipGuyWire_' + k, [0, 1.38, 0], [0, outerTipY, outerTipZ], 0.0035, goldHardware, {
      parent: midBoomJoint
    });
  }

  return root;
}

// ===========================================================================
// HELPER: Triangular Lattice Spar Construction
// ===========================================================================
function buildLatticeSparSegment(name, yStart, yEnd, radius, trussMat, accentMat, parent) {
  const length = yEnd - yStart;
  const numBays = 6;
  const bayLen = length / numBays;
  const r0 = radius;
  const r1 = radius * 0.75; // Subtle taper towards outer end

  // 3 longeron carbon tubes forming a triangular prism
  const longeronAngles = [0, 120, 240];
  const longeronPts = [];

  for (let a = 0; a < 3; a++) {
    const deg = longeronAngles[a];
    const rad = (deg * Math.PI) / 180;
    const x0 = r0 * Math.sin(rad);
    const z0 = r0 * Math.cos(rad);
    const x1 = r1 * Math.sin(rad);
    const z1 = r1 * Math.cos(rad);

    beamBetween(name + '_Longeron_' + a, [x0, yStart, z0], [x1, yEnd, z1], 0.007, trussMat, { parent });

    const pts = [];
    for (let b = 0; b <= numBays; b++) {
      const t = b / numBays;
      const r = r0 + t * (r1 - r0);
      pts.push([r * Math.sin(rad), yStart + t * length, r * Math.cos(rad)]);
    }
    longeronPts.push(pts);
  }

  // Cross battens and diagonal cross-braces for each lattice bay
  for (let b = 0; b < numBays; b++) {
    for (let s = 0; s < 3; s++) {
      const sNext = (s + 1) % 3;
      const p0 = longeronPts[s][b];
      const p1 = longeronPts[sNext][b];
      const pNext0 = longeronPts[s][b + 1];
      const pNext1 = longeronPts[sNext][b + 1];

      // Batten ring strut
      beamBetween(name + '_Batten_' + b + '_' + s, p0, p1, 0.005, trussMat, { parent });

      // Diagonal cross brace
      beamBetween(name + '_Diag_' + b + '_' + s, p0, pNext1, 0.004, accentMat, { parent });
    }
  }
}

// ===========================================================================
// HELPER: Origami-Folded Solar Foil Mesh (Miura-Ori Faceted Tessellation)
// ===========================================================================
function createOrigamiSailPanel(opts) {
  const { name, rMin, rMax, angleRad, numRings, numRays, foldDepth, matGold, matCopper, parent } = opts;

  // Grid coordinates
  const grid = [];
  for (let i = 0; i <= numRings; i++) {
    const u = i / numRings;
    const r = rMin + u * (rMax - rMin);
    const row = [];
    for (let j = 0; j <= numRays; j++) {
      const v = j / numRays;
      const theta = v * angleRad;

      // In the sail local frame:
      // +Y is along the boom spar (radial)
      // +Z is transverse across the sail quadrant
      // +X is out-of-plane origami fold displacement
      const y = r * Math.cos(theta);
      const z = r * Math.sin(theta);

      // Origami mountain/valley crease displacement
      const parity = ((i + j) % 2 === 0) ? 1 : -1;
      // Edge weight: 0 at spar attachment (j=0) so the edge mounts flush to the straight spar batten!
      const edgeWeight = Math.sin(v * Math.PI * 0.5);
      const radialScale = 0.5 + 0.5 * u;
      const x = parity * foldDepth * edgeWeight * radialScale;

      row.push([x, y, z]);
    }
    grid.push(row);
  }

  // Generate planar faceted triangles
  const posGold = [];
  const idxGold = [];
  const posCopper = [];
  const idxCopper = [];

  function addTri(posArr, idxArr, pA, pB, pC) {
    const base = posArr.length / 3;
    posArr.push(pA[0], pA[1], pA[2], pB[0], pB[1], pB[2], pC[0], pC[1], pC[2]);
    idxArr.push(base, base + 1, base + 2);
  }

  for (let i = 0; i < numRings; i++) {
    for (let j = 0; j < numRays; j++) {
      const p00 = grid[i][j];
      const p10 = grid[i + 1][j];
      const p11 = grid[i + 1][j + 1];
      const p01 = grid[i][j + 1];

      // Material pattern: geometric gold/copper alternating chevrons
      const isGold = ((Math.floor(i / 2) + Math.floor(j / 2)) % 2 === 0);
      const targetPos = isGold ? posGold : posCopper;
      const targetIdx = isGold ? idxGold : idxCopper;

      // Rigid origami fold crease diagonal alternates with parity
      if ((i + j) % 2 === 0) {
        addTri(targetPos, targetIdx, p00, p10, p11);
        addTri(targetPos, targetIdx, p00, p11, p01);
      } else {
        addTri(targetPos, targetIdx, p00, p10, p01);
        addTri(targetPos, targetIdx, p10, p11, p01);
      }
    }
  }

  if (posGold.length > 0) {
    const geoGold = meshGeo({ positions: posGold, indices: idxGold });
    createPart(name + '_Gold', geoGold, matGold, { parent });
  }
  if (posCopper.length > 0) {
    const geoCopper = meshGeo({ positions: posCopper, indices: idxCopper });
    createPart(name + '_Copper', geoCopper, matCopper, { parent });
  }

  // Slender perimeter gold trim along the origami zigzag outer edge
  for (let i = 0; i < numRings; i++) {
    const pA = grid[i][numRays];
    const pB = grid[i + 1][numRays];
    beamBetween(name + '_EdgeTrim_' + i, pA, pB, 0.003, matGold, { parent });
  }
}

// ===========================================================================
// ANIMATION: Solar Sail Deployment Sequence
// ===========================================================================
function animate(root) {
  const tracks = [];
  const duration = 3.0;

  for (let k = 0; k < 4; k++) {
    // 1. Primary Boom Deployment
    // Folds tightly along fuselage at t=0, swings outward to radial position
    tracks.push(
      rotationTrack('Joint_Boom_' + k, [
        { time: 0.0, rotation: [0, 0, -78] },
        { time: 0.8, rotation: [0, 0, -32] },
        { time: 1.6, rotation: [0, 0, 0] },
        { time: 3.0, rotation: [0, 0, 0] }
      ])
    );

    // 2. Mid-Boom Articulation
    // Tucked inward during cruise, locks straight during deployment
    tracks.push(
      rotationTrack('Joint_MidBoom_' + k, [
        { time: 0.0, rotation: [0, 0, 24] },
        { time: 0.8, rotation: [0, 0, 10] },
        { time: 1.6, rotation: [0, 0, 0] },
        { time: 3.0, rotation: [0, 0, 0] }
      ])
    );

    // 3. Inner Origami Sail Panel Hinge
    // Folded against the boom, unfurls radially
    tracks.push(
      rotationTrack('Joint_SailInner_' + k, [
        { time: 0.0, rotation: [0, 0, 75] },
        { time: 0.9, rotation: [0, 0, 35] },
        { time: 1.8, rotation: [0, 0, 0] },
        { time: 3.0, rotation: [0, 0, 0] }
      ])
    );

    // 4. Outer Origami Sail Panel Hinge
    // Tightly folded accordion over inner panel, blooms wide into full sail
    tracks.push(
      rotationTrack('Joint_SailOuter_' + k, [
        { time: 0.0, rotation: [0, 0, -135] },
        { time: 0.9, rotation: [0, 0, -60] },
        { time: 1.8, rotation: [0, 0, 0] },
        { time: 3.0, rotation: [0, 0, 0] }
      ])
    );
  }

  return [createClip('Deploy', duration, tracks)];
}

Brief and retained revisions

Brief summary

Create a solar-sail courier with folded copper and gold foil panels, visible hinges, slender structural spars and a compact ivory cargo core. Use custom folded surfaces and animate panel deployment.

  1. Initial design

    First retained courier with folded foil panels, articulated spars and a cargo core.

    Download initial design source

    SHA-256: 1fe4ad118242dd9805a88b09b27b90f9f3bd72b39cfcff8ad631646c56bf3fc1

  2. Sail and equipment revision

    Widened the sail panels and added edge trim, rigging, a communications dish and radiator fins.

    Download sail and equipment revision source

    SHA-256: 272e8a7af1693d89cc93388d2e6c6c5bfedd3d856dd58de4fb8a4c6e16476cba

  3. Current gallery revision · Shown here

    Repositioned the radiator fins, added a docking collar and hinge actuators, and adjusted the panel edges.

    Download current gallery revision source

    SHA-256: 0a3aac41c77094e96aaeed3414802b20fb3143398715cd8ffcc008d626821164