← Basics3D assetfogon/escalera-caracol
Escalera caracol
Drag to turn it// «Escalera caracol» · gpt-6-sol (medium)
// Spiral staircase («Escalera caracol»).
// Spiral staircase: pale stone ground ring, dark central column, and warm wooden steps.
const cx = 24, cy = 24;
const steps = 27;
const start = -1.10;
const turn = 0.32;
const rise = 1.22;
const outerR = 13;
// Thin, slightly irregular stone circle on the ground.
for (let x = 4; x <= 44; x++) {
for (let y = 7; y <= 41; y++) {
const d = Math.hypot((x - cx) / 19.2, (y - cy) / 16.1);
if (d > 0.905 && d < 1.025) {
set(x, y, 0, rand() < 0.14 ? "#859b98" : d > 0.975 ? "#a6b8b2" : "#c0cdc6");
if (d > 0.975 && rand() < 0.28) set(x, y, 1, "#d4dcd3");
}
}
}
// Square, layered base and substantial blue-black central newel.
box(20, 20, 0, 28, 28, 1, "#263b3c");
box(21, 21, 2, 27, 27, 3, "#355454");
box(22, 22, 4, 26, 26, 39, "#193840");
box(22, 22, 4, 22, 26, 39, "#315860");
box(23, 22, 4, 25, 22, 39, "#284b52");
box(26, 23, 4, 26, 26, 39, "#102d35");
for (let z = 5; z < 39; z += 3) {
box(21, 21, z, 27, 27, z, z % 2 ? "#31535a" : "#23464e");
box(22, 21, z + 1, 25, 21, z + 1, "#41636a");
}
box(21, 21, 39, 27, 27, 40, "#21434b");
box(22, 22, 41, 26, 26, 41, "#34575c");
box(23, 23, 42, 25, 25, 42, "#b88744");
function point(a, r) {
return [Math.round(cx + Math.cos(a) * r), Math.round(cy + Math.sin(a) * r)];
}
function height(i) {
return Math.round(5 + i * rise);
}
function angleDiff(a, b) {
return Math.atan2(Math.sin(a - b), Math.cos(a - b));
}
// Solid wedge-shaped treads. Each has a shaded underside, golden top and dark outer lip.
for (let i = 0; i < steps; i++) {
const a = start + i * turn;
const z = height(i);
const minX = Math.floor(cx - outerR - 1), maxX = Math.ceil(cx + outerR + 1);
const minY = Math.floor(cy - outerR - 1), maxY = Math.ceil(cy + outerR + 1);
for (let x = minX; x <= maxX; x++) {
for (let y = minY; y <= maxY; y++) {
const r = Math.hypot(x - cx, y - cy);
const off = angleDiff(Math.atan2(y - cy, x - cx), a);
if (r < 2.7 || r > outerR || Math.abs(off) > 0.183) continue;
set(x, y, z - 2, "#654728");
set(x, y, z - 1, r > 11.6 ? "#925e2b" : "#ae7132");
let wood = rand() < 0.13 ? "#d59546" : rand() < 0.19 ? "#f5bf69" : "#e9ad55";
if (off < -0.12) wood = "#f4c477";
set(x, y, z, wood);
if (r > 12.1) set(x, y, z - 1, "#9c632c");
}
}
// A chunky bracket beneath each outer end.
const [bx, by] = point(a, 9.5);
box(bx - 1, by - 1, z - 3, bx + 1, by + 1, z - 2, "#694729");
const [ix, iy] = point(a, 4.4);
line(ix, iy, z - 3, bx, by, z - 3, 0, "#80552e");
}
// Outer balusters: dark teal ironwork with pale edges and warm wooden caps.
for (let i = 0; i < steps; i += 3) {
const a = start + i * turn;
const [x, y] = point(a, 12.7);
const z = height(i);
box(x, y, z + 1, x + 1, y + 1, z + 7, "#193940");
box(x, y, z + 2, x, y, z + 6, "#365860");
box(x, y, z + 7, x + 1, y + 1, z + 8, "#c4873c");
set(x, y, z + 9, "#f1bf6a");
}
// A last post at the upper landing.
{
const a = start + (steps - 1) * turn;
const [x, y] = point(a, 12.7);
const z = height(steps - 1);
box(x, y, z + 1, x + 1, y + 1, z + 8, "#24454b");
box(x, y, z + 8, x + 1, y + 1, z + 8, "#d59849");
}
// Continuous stepped handrail and a lower cross rail following the spiral.
for (let i = 0; i < steps - 1; i++) {
const a = start + i * turn;
const b = a + turn;
const [x0, y0] = point(a, 12.8);
const [x1, y1] = point(b, 12.8);
const z0 = height(i), z1 = height(i + 1);
line(x0, y0, z0 + 8, x1, y1, z1 + 8, 0, "#b77b37");
line(x0, y0, z0 + 9, x1, y1, z1 + 9, 0, "#edb65e");
line(x0, y0, z0 + 4, x1, y1, z1 + 4, 0, "#a87134");
// Short upright spindle between the rails on alternate steps.
if (i % 2 === 1) {
const [sx, sy] = point(a + turn * 0.5, 12.8);
const sz = Math.round((z0 + z1) / 2);
box(sx, sy, sz + 4, sx, sy, sz + 8, "#24434a");
}
}
// Visible golden nosing at the first and last steps.
for (const i of [0, steps - 1]) {
const a = start + i * turn;
const z = height(i);
for (let r = 5; r <= 12; r++) {
const [x, y] = point(a + 0.16, r);
set(x, y, z, "#f4c476");
}
}import { draw } from "./assets/voxel";
import { escaleraCaracol } from "./assets/escalera-caracol";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, escaleraCaracol, x, y, { size: 64, angle: 0, t: performance.now() / 1000 });
// size: its longest side, in px
// angle: turn on the floor, in radians
// t: seconds; if it moves, that animates it# In a terminal, in the game's folder
fogon assets add escalera-caracol
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["escalera-caracol"] })
# It lands in client/assets/escalera-caracol.ts
# https://fogon.page/object/escalera-caracol