Skip to content
Kiln0.10Get Kiln

Habitat ring, solar arrays and docking modules

Earlier examples from earlier Kiln versions.

Historical gallery render of Orbital station; 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. 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
15,040
Estimated draws
635
Materials
5
Textures
0
Animation clips
1
Bounds X × Y × Z
13.23 × 20.5 × 38.6 m
Build warnings
0

Measurements come from this build.

Download this build

Runtime: 296,148 bytes. Original: 295,916 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
fcbb6f16c0be5f40e530f54f6037c8445db656d75137d87cb8a2e744e2778012
Original GLB
6d37050da2ec28dc56148326aef2c56a39cc10e67d901d908afde910ffeddbe6
Source
cac72b1669d259d4083d1f094dfd6c49d842f03ef45e509d6b24f70672cd55fd

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

GPT-6 Astra through Codex

Source-header credit

Source access
Existing station source and repository implementation available during this refinement.
Inherited context
Owner screenshot and feedback on the rear silhouette.
Starting example
Original orbital station, retained in gallery revision history.
Human input
Owner art direction; source refinement authored by Codex.
Authoring review
Refinement reviewed separately from the original authoring session.

Gallery GPU render of this exact source. Source, artifact, image hashes and camera settings are recorded alongside the poster; the artifact hash names the GLB bytes the image was rendered from, which the downloadable rebuild reproduces byte for byte only on the platform that recorded it.

Poster camera and render record

The source behind this build

orbital-station.kiln.js
// Authored by: gpt-6-astra, via codex.
//
// Original model-authored station retained in site/examples/history/orbital-station.
// Maintainer revision: Codex refinement following owner feedback on the rear assembly.
// This revision uses the existing source and repository context.

const meta = { name: 'OrbitalStation', category: 'prop' };
function build() {
 const root=createRoot('OrbitalStation');
 const white=gameMaterial(0xbfcbd0,{metalness:0.22,roughness:0.42,flatShading:false}), dark=gameMaterial(0x202d38,{metalness:0.65,roughness:0.38,flatShading:false}), blue=gameMaterial(0x153450,{metalness:0.55,roughness:0.26,flatShading:false}), glass=gameMaterial(0x4393a5,{metalness:0.25,roughness:0.23,emissive:0x174857,emissiveIntensity:0.18,flatShading:false}), gold=gameMaterial(0xa67b44,{metalness:0.72,roughness:0.34,flatShading:false});
 const frame=createPivot('Station',[0,10.25,0],root);
 const rotor=createPivot('HabitatRotor',[0,0,0],frame);
 const part=(n,g,m,p,parent=frame,r=[0,0,0])=>createPart(n,g,m,{position:p,parent,rotation:r});
 const beam=(n,a,b,r,m,parent=frame)=>beamBetween(n,a,b,r,m,{segments:6,parent});
 const ringPoint=(x,r,a)=>[x,r*Math.cos(a),r*Math.sin(a)];
 function wedge(r0,r1,x0,x1,a0,a1) {
  const v=[];for(const x of [x0,x1])for(const r of [r0,r1])for(const a of [a0,a1])v.push(...ringPoint(x,r,a));
  const g=new THREE.BufferGeometry();g.setAttribute('position',new THREE.Float32BufferAttribute(v,3));
  g.setIndex([0,1,3,0,3,2,4,6,7,4,7,5,0,4,5,0,5,1,2,3,7,2,7,6,0,2,6,0,6,4,1,5,7,1,7,3]);const solid=g.toNonIndexed();solid.computeVertexNormals();return solid;
 }
 for(let i=0;i<24;i++){
  const a=i*Math.PI/12, b=(i+1)*Math.PI/12, mid=(a+b)/2;
  part('HabitatWedge_'+i,wedge(8.1,9.9,-0.9,0.9,a,b),i%6===0?gold:white,[0,0,0],rotor);
  for(const side of [-1,1]){
   part('WindowBand_'+i+'_'+side,wedge(8.65,9.2,side>0?0.899:-0.93,side>0?0.93:-0.899,a+0.035,b-0.035),glass,[0,0,0],rotor);
   part('WindowMullion_'+i+'_'+side,boxGeo(0.06,0.57,0.055),white,ringPoint(side*0.942,8.925,mid),rotor,[mid*180/Math.PI,0,0]);
   beam('Handrail_'+i+'_'+side,ringPoint(side*1.22,9.65,a),ringPoint(side*1.22,9.65,b),0.045,dark,rotor);
   beam('RailStanchion_'+i+'_'+side,ringPoint(side*0.86,9.65,a),ringPoint(side*1.22,9.65,a),0.045,dark,rotor);
  }
  beam('OuterCoolant_'+i,ringPoint(0,10.15,a),ringPoint(0,10.15,b),0.1,gold,rotor);
  part('CableTray_'+i,boxGeo(0.34,0.12,2.56),dark,ringPoint(-0.5,9.96,mid),rotor,[mid*180/Math.PI,0,0]);
  if(i%2===0){
   part('ServiceJunction_'+i,boxGeo(0.65,0.22,0.5),white,ringPoint(0.25,9.9,mid),rotor,[mid*180/Math.PI,0,0]);
   part('ServiceLatch_'+i,boxGeo(0.18,0.04,0.3),gold,ringPoint(0.25,10.02,mid),rotor,[mid*180/Math.PI,0,0]);
  }
  beam('PipeClamp_'+i,ringPoint(0,9.8,mid),ringPoint(0,10.15,mid),0.12,dark,rotor);
 }
 for(let i=0;i<4;i++){
  const a=i*Math.PI/2, p=createPivot('SpokeFrame'+i,[0,0,0],rotor);p.rotation.x=a;
  part('TaperedSpoke'+i,cylinderGeo(0.65,0.32,6.9,8),white,[0,4.95,0],p);
  beam('SpokeService'+i,[0.4,1.4,0],[0.4,8.2,0],0.08,gold,p);
  for(let j=0;j<3;j++)part('SpokeFlange'+i+'_'+j,boxGeo(1.12,0.15,1.12),dark,[0,3+j*2,0],p);
 }
 const hubShell=lathe([[0,-3.2],[1.2,-3.2],[1.35,-3.04],[1.35,3.04],[1.2,3.2],[0,3.2]],48);hubShell.rotateZ(-Math.PI/2);
 part('FixedHub',hubShell,white,[0,0,0]);
 part('RotorBearing',cylinderXGeo(1.58,1.58,1.2,48),dark,[0,0,0]);
 for(const x of [-2.9,2.9])part('HubEndBand'+x,cylinderXGeo(1.43,1.43,0.22,48),gold,[x,0,0]);
 // A recessed axial service port, surrounded by a structural annulus.
 // The dark backing sits behind the lip, so the end reads as a cavity rather than a blank cap.
 part('AftPressureBulkhead',cylinderXGeo(1.22,1.35,0.48,48),dark,[-3.38,0,0]);
 part('AftPortRecess',cylinderXGeo(0.84,0.84,0.08,48),dark,[-3.66,0,0]);
 const aftRing=(name,radius,tube,x,mat)=>part(name,torusGeo(radius,tube,8,48),mat,[x,0,0],frame,[0,90,0]);
 aftRing('AftStructuralLip',1.16,0.16,-3.68,white);
 aftRing('AftSeal',0.91,0.07,-3.72,gold);
 part('ServiceHatch',cylinderXGeo(0.64,0.64,0.06,32),white,[-3.73,0,0]);
 part('HatchSplit',boxGeo(0.025,1.08,0.035),dark,[-3.77,0,0]);
 for(let i=0;i<8;i++){
  const a=i*Math.PI/4;
  part('AftLatch'+i,boxGeo(0.18,0.2,0.16),gold,ringPoint(-3.83,1.13,a),frame,[a*180/Math.PI,0,0]);
  part('HubServicePanel'+i,boxGeo(1.4,0.12,0.55),white,ringPoint(-1.98,1.34,a),frame,[a*180/Math.PI,0,0]);
  for(let j=0;j<4;j++)part('HubVent'+i+'_'+j,boxGeo(0.065,0.025,0.38),dark,ringPoint(-2.42+j*0.23,1.415,a),frame,[a*180/Math.PI,0,0]);
 }
 const dock=createPivot('DockingNode',[3.7,0,0],frame);
 part('DockingManifold',cylinderXGeo(0.95,1.15,1.4,12),dark,[0,0,0],dock);
 for(let i=0;i<4;i++){
  const a=i*Math.PI/2;
  beam('RadialDockNeck'+i,ringPoint(0,0.7,a),ringPoint(0,1.65,a),0.42,white,dock);
  const p=createPivot('DockCollarFrame'+i,ringPoint(0,1.7,a),dock);p.rotation.x=a-Math.PI/2;
  part('RadialDockCollar'+i,torusGeo(0.48,0.12,6,12),gold,[0,0,0],p);
 }
 part('AxialDockCollar',cylinderXGeo(0.58,0.58,0.55,12),gold,[4.6,0,0]);
 const craft=createPivot('SupplyCraft',[6.3,0,0],frame);
 part('CargoHull',cylinderXGeo(0.86,0.86,2.9,12),white,[0,0,0],craft);
 part('CargoForwardCap',coneXGeo(0.86,0.75,12),white,[1.8,0,0],craft);
 for(const x of [-1,0.8])part('CargoStrap'+x,cylinderXGeo(0.9,0.9,0.18,12),dark,[x,0,0],craft);
 part('Cockpit',boxGeo(0.7,0.14,0.85),glass,[1,0.82,0],craft);
 for(const s of [-1,1]){
  part('ThrusterPod'+s,cylinderXGeo(0.24,0.3,0.9,8),dark,[-0.8,0,s*0.92],craft);
  beam('ArrayBoom'+s,[-2.5,0,0],[-2.5,0,s*11.4],0.18,white);
  part('GimbalBearing'+s,cylinderZGeo(0.5,0.5,0.8,12),gold,[-2.5,0,s*11.2]);
  const wing=createPivot('SolarWing'+s,[-2.5,0,s*15.2],frame);wing.rotation.z=-0.85;
  part('ArrayBacking_'+s,boxGeo(4.6,0.16,8.2),dark,[0,0,0],wing);
  for(let row=0;row<12;row++)for(let col=0;col<4;col++)
   part('SolarCell_'+s+'_'+row+'_'+col,boxGeo(1.06,0.035,0.6),blue,[-1.68+col*1.12,0.097,-3.63+row*0.66],wing);
  for(const x of [-2.27,0,2.27])beam('ArraySpar_'+s+'_'+x,[x,-0.36,-4.1],[x,-0.36,4.1],0.055,white,wing);
  for(const x of [-2.27,0,2.27])for(const z of [-4,4])beam('TrussTie_'+s+'_'+x+'_'+z,[x,-0.36,z],[x,0,z],0.055,white,wing);
  for(let j=0;j<8;j++){
   beam('ArrayCrossbar_'+s+'_'+j,[-2.3,-0.12,-4+j*1.14],[2.3,-0.12,-4+j*1.14],0.045,white,wing);
   beam('ArrayTruss_'+s+'_'+j,[-2.25,-0.36,-4+j],[2.25,-0.12,-3+j],0.045,white,wing);
  }
  // Mirrored, double-sided thermal cassettes, with a visible load path to the hub.
  const rad=createPivot('Radiator'+s,[-1.95,s*2.65,0],frame);
  for(const z of [-0.9,0.9]){
   beam('RadiatorRoot'+s+'_'+z,[-1.2,s*0.92,z*0.65],[-1.2,s*2.65,z],0.11,dark);
   beam('RadiatorBrace'+s+'_'+z,[-2.85,s*0.92,z*0.65],[-1.2,s*2.65,z],0.075,gold);
  }
  part('ThermalCassette_'+s,boxGeo(2.55,0.2,3.5),dark,[0,0,0],rad);
  for(const face of [-1,1]){
   for(let j=0;j<6;j++){
    part('ThermalTile_'+s+'_'+face+'_'+j,boxGeo(0.37,0.045,3.18),white,[-1.025+j*0.41,face*0.12,0],rad);
    part('ThermalTube_'+s+'_'+face+'_'+j,boxGeo(0.025,0.025,3.05),dark,[-1.025+j*0.41,face*0.155,0],rad);
   }
  }
  for(const z of [-1.7,1.7])part('RadiatorHeader'+s+'_'+z,boxGeo(2.62,0.25,0.12),gold,[0,0,z],rad);
  for(const x of [-1.28,1.28])part('RadiatorEdge'+s+'_'+x,boxGeo(0.09,0.24,3.5),white,[x,0,0],rad);
 }
 // Paired communications pods flank the aft port. No isolated top mast or horn:
 // the rear service assembly is mirrored across both its vertical and lateral axes.
 for(const s of [-1,1]){
  beam('CommsOutrigger'+s,[-2.9,0,s*1.2],[-4.15,0,s*2.05],0.12,dark);
  for(const y of [-0.35,0.35])beam('CommsBrace'+s+'_'+y,[-2.5,y,s*1.18],[-4.15,0,s*2.05],0.055,gold);
  const pod=createPivot('CommsPod'+s,[-4.15,0,s*2.05],frame);pod.rotation.x=s*Math.PI/2;
  part('CommunicationsDish'+s,lathe([[0,0],[0.2,0.025],[0.43,0.13],[0.6,0.29],[0.6,0.35],[0.42,0.19],[0.18,0.085],[0,0.06]],32),white,[0,0,0],pod);
  beam('DishFeed'+s,[0,0,0],[0,0.5,0],0.035,gold,pod);
 }
 root.updateMatrixWorld(true);
 const bounds=new THREE.Box3().setFromObject(root);frame.position.y-=bounds.min.y;
 return root;
}
function animate(){ return [createClip('HabitatRotation',60,[rotationTrack('Joint_HabitatRotor',[0,15,30,45,60].map((time,i)=>({time,rotation:[i*90,0,0]})))])]; }

Brief and retained revisions

Brief summary

Refine the orbital station rear assembly into a more appealing hero asset while preserving the ring silhouette.

  1. Original station

    Original model-authored station before owner feedback on the rear assembly.

    Download original station source

    SHA-256: d2c1693ef6938a14e482a3640c838bbf17c721aa333a7559ad496593ae1f33ea

  2. Rear assembly refinement · Shown here

    Owner-directed Codex refinement: mirrored radiator cassettes and communications pods, recessed service hatch, supported joints and smoother material finish. Existing source and repository context were available.

    Download rear assembly refinement source

    SHA-256: cac72b1669d259d4083d1f094dfd6c49d842f03ef45e509d6b24f70672cd55fd