greenhouse
Skill4 starsChanged 16 days agoMITView on GitHub
Build a believable procedural space or sci-fi greenhouse in Three.js: a ribbed pressurised glazing dome with airlock, catwalk and nutrient core, plus tiered hydroponic grow rows with crops, trays and LED bars. Use for a lunar or Mars habitat, an orbital farm, a research biodome, or any interior where food is grown under lamps.
2 other projects use a version of this.
Adopted by
Most recently taken up by
- oliverweissl/context-gardentheir own version, first seen Nov 8, 2026
- apideck-libraries/api-skillstheir own version, first seen Aug 13, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- apideck-libraries/api-skills★ 4same name
- oliverweissl/context-garden★ 4same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository28 other files it gives its agents.
- character-cameraSkill
- character-inputSkill
- character-kitSkill
- character-modelSkill
- character-walkingSkill
- engineered-environmentsSkill
- megacitySkill
- station-maglevSkill
- pipesSkill
- procedural-geometrySkill
- signageSkill
- indirect-lightingSkill
- lightingSkill
- glassSkill
- interior-mappingSkill
- material-compositionSkill
- metalSkill
- paintSkill
- rustSkill
- tsl-materialsSkill
- waterSkill
- threejs-directorSkill
- card-grassSkill
- far-shadeSkill
- ocean-waterSkill
- photo-skySkill
- snow-weatherSkill
- jolt-drivingSkill
The file
We split this skill into the rules it gives an agent
7 rules. About 0 are common, 5 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 16
Use these words consistently in code, object names and prose:
- Distinctive: line 88
Every rib lands on the tension ring.
- Distinctive: line 91
No interior wall or mezzanine that would cut the pressure envelope.
- Distinctive: line 116
Every row has a feed and a drain.
- Very specific: line 118
Use crop: 'none' for a clean rack in a cutaway or for a row awaiting planting.
- Distinctive: line 179
Run the branch along the aisle at the feed height, then a short bend into each row.
- Very specific: line 195
Add one or two real lights for the pools on the floor and the core, not one per bar.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Greenhouse
Target: Three.js 0.187.0, WebGPU renderer (three/webgpu) and TSL node materials (three/tsl); WebGPU only, no WebGL fallback, no GLSL strings. Everything is generated by code; no model files or textures.
A greenhouse is a working closed-loop machine that happens to be full of plants, not a garden conservatory. Everything in it exists to keep a crop alive: the pressure envelope, the airlock, the drained floor, the nutrient core, the lamps, the rows, the service walkways. Build those first; the greenery is the result.
scripts/greenhouse.js registers two procedural models, "Pressurised greenhouse dome" (greenhouse-dome) and "Hydroponic grow row" (grow-row), and exports a bay layout and a whole-greenhouse helper. It is written against the procedural-models kernel shipped with the pipes skill (procedural-models.js); copy both files into the project together. Distribution pipework is not modelled here: use the pipes kit, and meet it at the feed points the layout returns.
Vocabulary
Use these words consistently in code, object names and prose:
| Term | Meaning |
| pressure envelope | the glazed shell that holds the atmosphere: a ribbed ellipsoid, not a conservatory roof |
| tension ring | the base ring beam and plinth that take the envelope's outward thrust |
| airlock / vestibule | the two-door pressure threshold on the door axis; the dome's front door |
| catwalk | the perimeter service walkway at mid-height, with grating, rails and a ladder |
| nutrient core | the central tank and ring main that receives drain water and returns solution |
| grow row | one freestanding tiered rack; the repeated unit of production |
| bay / ring | an angular slice of the dome; rows sit tangentially in an inner and an outer ring |
| feed | the world point where a row's drip manifold meets a branch from pipes |
When to use it
- a pressurised agricultural dome on a moon, Mars, an asteroid or a station
- a research biodome, a seed vault, a generation-ship farm, a terraforming camp
- an interior where the grow rows are the subject: a hydroponics hall, a vertical farm, a biotech lab
- a cutaway or diorama where the dome and the rows must read at the same scale
Not for: a domestic conservatory or a botanical garden (use architectural glazing and planting instead), a field or orchard (outdoor kits and card-grass), or a single potted plant. For the shell materials read the glass skill; for the lamps read lighting; for the pipes read pipes.
What ships
| Export | What it is |
greenhouseDome | model definition, id greenhouse-dome |
growRow | model definition, id grow-row |
registerGreenhouse() | registers both; returns their ids |
layoutRows(spec) | rows in concentric bays; one entry per row with spec, matrix, feed, ring, bay, angle |
createGreenhouse(spec, opts) | dome plus every row as one THREE.Group; userData carries dome, rows, feeds, dispose() |
greenhouseMaterials(def, opts) | layer description to material; adds physical transmission glass and double-sided leaves |
FLOOR_Y | top of the dome floor above the model origin, 0.55 m |
import { registerModel, createModel, createField } from './procedural-models.js';
import { registerGreenhouse, createGreenhouse, layoutRows, greenhouseMaterials } from './greenhouse.js';
registerGreenhouse();
scene.add(createModel('greenhouse-dome', { diameter: 24, ribs: 24, bands: 6 }));
scene.add(createModel('grow-row', { length: 6, tiers: 4, crop: 'lettuce' }));
const whole = createGreenhouse({ diameter: 24, bays: 8, innerBays: 5 });
scene.add(whole);The dome
Envelope
- A ribbed ellipsoidal pressure envelope, not a hemisphere:
riseis the crown height as a fraction of the radius, default0.8. A 24 m dome rises 9.6 m. The shallow ellipse reads as a pressure vessel and leaves more usable height at the rim than a hemisphere. - Meridian ribs every 15° (
ribs12–32, default 24), inbandsrings (3–8, default 6). The glazing is flat trapezoidal panels between the ribs and ring beams, inset a few centimetres, with a visible gasket gap. Flat panels are what a real prefabricated glazing system uses; a continuous glass skin reads as a bubble. - Tension ring at the base: a steel ring beam on a concrete plinth, with anchor nuts. The ring takes the outward thrust of the envelope, so it must be continuous and heavier than the ribs.
- Crown compression ring and vent lantern: a ring at the top, a short drum and a mushroom cap. The lantern is the pressure relief and heat exhaust; it is also the silhouette that says "habitat" rather than "observatory".
- Floor: a drained concrete slab 0.55 m above grade, with a perimeter drain channel and painted walkway lines. The slab is inset from the tension ring, leaving a service trough around the edge.
- Panel glazing can be
clear,frostorsolar. Clear is nearly colourless; frost has a rough, milky transmission; solar is green-grey and absorbs heat. Use one choice for the whole envelope.
Openings and circulation
- One airlock on the door axis, a vestibule about 2.75 m square by 2.55 m high, with an outer and an inner door, a porthole, a status lamp and steps down to grade. A pressure envelope has no plain door: the airlock is the threshold, and it should be the first thing a viewer understands.
- The airlock is cut out of the lowest glazing bands, with a portal frame around the opening. Do not leave a glazing panel in front of the door.
- A perimeter catwalk at mid-height (about 4.8 m on the default dome), 1.05 m wide, on brackets off the shell, with grating, edge beams, toe boards, two handrails at 0.56 m and 1.08 m, posts every 1.5–2.5 m, and a ladder from the floor. The catwalk is how the lamps, vents and glazing are serviced.
- Aisles: the door axis is kept clear.
layoutRowsskips every bay within about half a bay of the door so the airlock opens onto a real aisle. - Mark the walkway and the drain with painted lines, not geometry: thin prisms a few millimetres above the slab are enough.
Core
A central nutrient core: a cylindrical tank about 2.2 m across, a domed lid, a level window, a control panel, a ring main around it and four risers. On top, a light mast with six high-bay fixtures aimed down at the inner beds. The core is the visual anchor under the crown; keep it below the catwalk so the dome reads as one volume.
Dome rules
- The envelope is continuous: no gap between glazing and ring, no panel floating off a rib.
- Every rib lands on the tension ring; every ring beam lands on a rib.
- The airlock is on the ground, the catwalk is above head height, and neither blocks the other.
- The floor is the working level inside the ring. The airlock floor meets it, and the steps go down to grade outside; do not recess the working floor below the door.
- No interior wall or mezzanine that would cut the pressure envelope. If the programme needs rooms, put them in separate pressure vessels.
The grow row
A freestanding tiered hydroponic rack, 2–12 m long, 0.6–1.2 m wide, 2–6 tiers. Defaults: 6 m, 0.9 m, 4 tiers, 0.65 m pitch.
- Frame: four corner posts on base plates, a crossbar at every tier and at the top, longitudinal shelf bars, and diagonal braces on the high-detail model. Painted steel, not aluminium extrusion.
- Trays: a U-channel profile with a 24 mm wall and 130 mm depth, one per tier, with a substrate slab 55 mm thick. The tray is a gutter, not a box: it has an open top and a lip.
Crop: lettuce, herbs, strawberry, seedling or none. Plants are built from folded leaf ribbons, a stem and (strawberry, high detail) small fruit. Spacing is by crop:
Crop Along row Across row Canopy height Lettuce 0.24 m 0.24 m 0.20 m Herbs 0.18 m 0.17 m 0.17 m Strawberry 0.26 m 0.24 m 0.16 m Seedlings 0.12 m 0.11 m 0.08 m - Light: one or two LED bars above each tray, not one per plant. The bar sits 0.17 m below the next tray and 0.58 m above the top canopy; a pink-magenta diffuser with a dark housing. The lamp is emissive geometry, not a real light per bar.
- Water: a drip manifold at one end, a branch and a distribution line along each tray, emitters every 0.32 m at high detail, an isolation valve on the first branch, and a tank and pump under the bottom shelf (or a floor stub when
tankis off). The manifold rises from the floor to above the top tray. - Clearance: the tank fits under the first tier with the pump on top; the lowest tray is at 0.42 m so the tank and the floor can be cleaned. Nothing hangs below the base plates.
Row rules
- The lamp is above the canopy and below the tray above: a plant must never grow into a fixture. If the crop is taller, raise the tier pitch, do not shrink the lamp gap.
- Trays overhang the frame by a few centimetres at most; a tray that overhangs the aisle is a head injury.
- Every row has a feed and a drain. A row that ends in mid-air is a diagram, not a machine.
- Rows are serviced from the aisles, so put the manifold and tank at the aisle end, not against a wall.
- Use
crop: 'none'for a clean rack in a cutaway or for a row awaiting planting; empty trays still need substrate and lamps.
Layout
layoutRows places rows tangentially in two concentric rings:
- inner ring at
0.35 R,innerBaysbays (default 5),tiers - 1tiers - outer ring at
0.68 R,baysbays (default 8), full tiers rowsPerBayrows per bay (default 2) atrowPitchcentres (default 1.15 m): a double-row bed serviced from both sides- row length =
clamp(chord × 0.78, 2.2, 10), rounded to 0.5 m, wherechord = 2 r sin(π / bays) - the bay containing the door is skipped, and crops alternate through
CROP_ORDERwhencrop: 'mixed'
The default 24 m dome gives 22 rows. Each row's matrix is a world transform with local +X along the row, +Y up and +Z radial; feed is the world point at the row's drip manifold, at FLOOR_Y + 0.21, ready to meet a branch.
const rows = layoutRows({ diameter: 24, bays: 8, innerBays: 5, crop: 'mixed' });
for (const row of rows) {
const m = createModel('grow-row', { ...row.spec, detail: 'high' }, { materials: greenhouseMaterials });
m.position.setFromMatrixPosition(row.matrix);
m.quaternion.setFromRotationMatrix(row.matrix);
scene.add(m);
}For many identical rows, build one createField('grow-row', spec, matrices) per crop and set distances: [45, 160]; call field.update(camera) whenever the camera moves. The field takes one spec for every instance, so group rows by crop and tier count rather than trying to vary them per instance.
Reuse the pipes kit
The dome ships only short service pipes around the core. The mains and branches belong to the pipes skill: that kit already models flanged modules, supports, valves, lagging and end bends, and the pipeRun recipe in that skill stretches a whole number of modules along a line.
import * as THREE from 'three/webgpu';
import { registerModel, createField } from './procedural-models.js';
import { pipeRack } from './pipe-rack.js';
import { layoutRows } from './greenhouse.js';
registerModel(pipeRack);
const rows = layoutRows({ diameter: 24 });
// The pipes module runs along local +Y and stands off along local +Z.
// Row local X is tangential, row local Z is radial; point +Y radially at the
// feed and +Z up, then lower the origin by the stand-off so the pipe centre
// lands at feed height.
const feedMatrices = rows.map(({ matrix, feed }) => new THREE.Matrix4()
.makeBasis(
new THREE.Vector3().setFromMatrixColumn(matrix, 0),
new THREE.Vector3().setFromMatrixColumn(matrix, 2),
new THREE.Vector3(0, 1, 0),
)
.setPosition(feed.x, feed.y - 0.3, feed.z));
const branches = createField('pipes', {
pipes: 1, diameter: 0.15, length: 3, spacing: 1.8, stand: 0.3,
supports: 'none', flanges: false, valve: false, bend: 'none', color: '#6C8E93',
}, feedMatrices);
scene.add(branches);
branches.update(camera);Rules for the join:
- Run the branch along the aisle at the feed height, then a short bend into each row. Do not run a main across a row or through the airlock aisle.
- A 3 m pipe module is the shortest the
pipeskit builds. Use it as the visible branch, place the feed at one end, and hide the last few centimetres behind the manifold. - Mains follow the catwalk edge or the service trough, never diagonally across the floor.
- One colour per service everywhere: water, nutrient solution, drain, air, power. The dome's
Service pipeslayer and thepipeskit must agree.
Materials and lighting
greenhouseMaterials(def) extends the kernel's createMaterial:
{ glass: true, color, rough, transmission, ior, thickness, attenuation?, attenuationDistance?, frost? }→MeshPhysicalNodeMaterialwithtransmission,thicknessandDoubleSide. Keeptransparent: false: transmission is an opaque pass in three, and settingtransparentreintroduces sorting artefacts.{ leaf: true, color, ... }→ the normal painted material withside: DoubleSide. Leaves need double-sided shading or half the canopy disappears.- Everything else passes through to the kernel (paint, metal, glow, coat).
Lighting:
- The LED diffusers and the high-bay lenses are emissive geometry (
glowaround 2.2). They read at any distance and cost nothing. - Add one or two real lights for the pools on the floor and the core, not one per bar. A dome with 40 point lights will not render in a reasonable frame; see
lightingfor clustered or baked alternatives. - Glass needs something to reflect: a sky, a starfield, an environment map. Without it, transmission reads as grey plastic.
- Shadows: the dome shell and the rows cast; the glazing is usually better as a shadow caster only at high detail, or not at all. The catwalk grating should not cast a solid slab shadow.
Performance
Measured on the defaults, one model at each level:
| Model | high | medium | low |
greenhouse-dome (24 m) | ~107k tris | ~36k | ~27k |
grow-row (6 m, 4 tiers, lettuce) | ~47k | ~15k | ~12k |
- High detail is for a hero shot or a cutaway; medium is the working level for a scene; low is for rows seen from across the dome.
- The dome is one draw call per material layer (15 layers), the row one per layer (12). Instancing rows with
createFieldis the only way to get hundreds. createGreenhouseshares one material cache across the dome and all rows.userData.dispose()frees the geometries and the materials; call it when the scene is torn down.- Glass panels at high detail are separate quads; at low detail one lathe replaces them. Do not add more glazing subdivisions than the rib count can explain.
Pitfalls
- The model must sit on y = 0. The dome's plinth and the row's base plates are the lowest geometry. If you edit a part, check the lowest vertex before shipping.
- The row's origin is the floor, not the first tray. Place rows at
FLOOR_Y + 0.01in the dome, or on a slab of your own. - Do not pass layout parameters to the dome model.
createGreenhousefilters them for you; if you callcreateModel('greenhouse-dome', spec)yourself, pass only the dome parameters. Unknown keys warn. - Do not scale the model non-uniformly. The glazing thickness, leaf normals and pipe radii all assume metres. Scale uniformly or rebuild with different parameters.
- Do not clone hundreds of rows.
clone()shares geometry but each clone is still a group of meshes; usecreateField. - Do not add a flat green sheet as a crop. The leaves are the crop; a green plane reads as AstroTurf.
- Do not put a real light in every fixture, and do not make the glass
transparent: true.
API
greenhouse-dome
| Parameter | Range | Default | Meaning |
diameter | 12–48 m | 24 | tension-ring diameter |
rise | 0.55–1.0 R | 0.8 | crown height / radius |
ribs | 12–32, step 4 | 24 | meridian ribs |
bands | 3–8 | 6 | glazing bands |
glazing | clear / frost / solar | clear | panel material |
airlock | toggle | on | vestibule, doors, steps, porthole |
catwalk | toggle | on | ring walkway, rails, ladder |
core | toggle | on | nutrient core, ring main, light mast |
color | colour | #C6CBC4 | frame paint |
detail | high / medium / low | high | level of detail |
grow-row
| Parameter | Range | Default | Meaning |
length | 2–12 m | 6 | rack length |
tiers | 2–6 | 4 | tray levels |
width | 0.6–1.2 m | 0.9 | tray width |
pitch | 0.45–0.9 m | 0.65 | tier spacing |
crop | lettuce / herb / strawberry / seedling / none | lettuce | planting |
lights | toggle | on | LED bars |
tank | toggle | on | tank and pump under the bottom shelf |
color | colour | #D8DAD3 | frame paint |
leafColor | colour | #4F8A3C | crop colour |
detail | high / medium / low | high | level of detail |
layoutRows(spec)
Accepts diameter, bays, innerBays, rowsPerBay, tiers, crop, rowPitch, color, leafColor. Returns [{ spec, matrix, feed, ring, bay, angle }]; spec is ready for grow-row, feed is a THREE.Vector3 in world space.
createGreenhouse(spec, opts)
spec accepts the layout keys plus the dome keys (rise, ribs, bands, glazing, airlock, catwalk, core). opts accepts detail and anything createModel accepts. Returns a group with userData.dome, userData.rows, userData.feeds and userData.dispose().
Research notes, precedents and the measurements behind these choices are in references/space-greenhouse-systems.md.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

