← Back to Gallery

Loading 3D viewer…

Vintage fairground carousel (guided)

by Jorge Barbero

Dimensions
72×52×72
Blocks
47,328
Compute Time
6.7s
Generation Time
3m 56s
Edits
2
Model
claude-opus-5
Created
September 1, 2026
carouselfairgroundmachineautoevalguided
Prompt

User Prompt

a vintage fairground carousel (merry-go-round): a twelve-sided striped conical canopy in cream and crimson with gold trim, a central mirrored column, ornate horses on brass poles, a scalloped valance with lantern lights, on a raised wooden platform in a park

Enhanced Prompt

guided condition: prepare_build (SMALL BUILD) + build-rules/Essential, small-builds, organic-builds/Horse proportions, block-palettes gradient. Dodecagon platform + striped 12-sector conical canopy + drawLine horses on chain poles + scalloped valance.

Photos

Exterior
Exteriorexterior
Deck and centre column
Deck and centre columninterior
Horses
Horsesinterior
Canopy and valance
Canopy and valanceinterior
CraftScript Source
// === BUILD PLAN ===
// Style: late-Victorian fairground carousel (merry-go-round) in a municipal park
// Footprint: REGULAR DODECAGON (12-sided), never a square:
//   deck r=13 at Y2 on a panelled skirt Y0-Y1, 3-step entry on the south side
// Height: deck Y2 | mounts Y3-Y8 | valance ring Y15-16 | striped cone Y16-25 | crown+flag Y26-29
//
// PLAN (top-down, X right, Z down) - 12 sectors, 8 mounts + 12 rim rods:
//            _ _ _ _ _
//         /   h  R  h   \        h = horse (cardinal, body along an axis)
//        / R           R \       H = horse (diagonal, body drawn on the diagonal)
//       | H     ###     H |      ### = mirrored centre column (r2, sea-lantern core)
//       | h    #####    h |      R  = rim rod carrying the canopy (12 of them)
//        \ H         H  /        S  = entry steps, south
//         \_ _ R h R _ _/
//                 S
//
// ELEVATION:
//        ^ flag                     canopy = 12 SECTORS, alternating cream / crimson
//      /\|/\                        stripes run UP the slope; gold trim ring at the rim
//    /=========\   <- gold ring     and a gold crown at the apex
//   /||||||||||\\  <- striped cone
//   ~^~^~^~^~^~^~  <- SCALLOPED VALANCE: upside-down stairs + slabs + hanging lanterns
//    |    ##    |  <- mirrored column, horses on brass poles between deck and canopy
//   ============== <- deck Y2
//   |____________| <- panelled skirt
//
// MATERIALS PLAN (what, where, WHY):
//   deck    : noisePalette spruce/dark_oak/stripped_spruce planks - WHY: worn fairground boards
//   skirt   : gradientPalette Y0->Y1 deepslate_bricks -> polished_andesite - WHY: shadow at ground = AO
//   canopy  : SECTOR stripes, cream = bone_block/white_terracotta/smooth_quartz,
//             crimson = red_terracotta/red_concrete/red_nether? no -> red_terracotta ramp
//             WHY: the stripe IS the silhouette-reading feature of a carousel
//   trim    : gold_block + waxed copper accents on the rim ring, crown and pole caps
//   column  : quartz + white_stained_glass mirror panels over a sea_lantern core - WHY: it glows
//   horses  : bone_block/white_terracotta body, gold mane, red_wool saddle, chain pole
// "Rooms": the covered deck IS the interior - centre column, 8 mounts, 2 chariot benches,
//          lantern ring, ticket kiosk outside. Every mount reachable from the entry steps.
// === END PLAN ===

var floorY = 0;
var deckY = 2, walkY = 3;
var rimY = 16, capH = 9;
var R_DECK = 13, R_CANOPY = 14;

function polyR(theta, R0, n) {
    var seg = Math.PI * 2 / n;
    var a = theta % seg;
    if (a < 0) a += seg;
    return R0 * Math.cos(Math.PI / n) / Math.cos(a - Math.PI / n);
}
function ang(x, z) { var t = Math.atan2(z, x); if (t < 0) t += Math.PI * 2; return t; }

// ---------- PARK GROUND ----------
var park = terrain({
    cx: 0, cz: 0, radius: 32, baseY: -1, amp: 2, scale: 0.05, seed: 91,
    irregularity: 0.35,
    surface: [{ block: "grass_block", w: 74 }, { block: "moss_block", w: 12 }, { block: "coarse_dirt", w: 8 }, { block: "podzol", w: 6 }],
    sub: "dirt", deep: "stone", depth: 4,
    pads: [{ cx: 0, cz: 0, r: 17, y: floorY, falloff: 9 }]
});

// ---------- SKIRT + DECK ----------
var skirtMat = gradientPalette(floorY, deckY, [
    { block: "deepslate_bricks", from: 0 },
    { block: "polished_deepslate", from: 0.35 },
    { block: "polished_andesite", from: 0.7 }
], { noise: 0.2, seed: 3 });
var deckMat = noisePalette([{ block: "spruce_planks", weight: 50 }, { block: "dark_oak_planks", weight: 28 }, { block: "stripped_spruce_wood", weight: 22 }], { seed: 12, freq: 0.22 });

var x, z, t, rr, dd;
for (x = -R_CANOPY - 2; x <= R_CANOPY + 2; x++) {
    for (z = -R_CANOPY - 2; z <= R_CANOPY + 2; z++) {
        dd = Math.sqrt(x * x + z * z);
        if (dd < 0.5) continue;
        t = ang(x, z);
        rr = polyR(t, R_DECK, 12);
        if (dd <= rr) {
            pb(x, floorY, z, skirtMat(x, floorY, z));
            pb(x, floorY + 1, z, skirtMat(x, floorY + 1, z));
            pb(x, deckY, z, deckMat(x, deckY, z));
        }
    }
}
pb(0, floorY, 0, "polished_deepslate"); pb(0, floorY + 1, 0, "polished_deepslate"); pb(0, deckY, 0, "polished_andesite");
// decorative skirt panelling: painted pilaster every sector edge
var si;
for (si = 0; si < 12; si++) {
    var a0 = (Math.PI * 2 / 12) * si;
    var pr = polyR(a0, R_DECK, 12);
    var px = Math.round(Math.cos(a0) * pr), pz = Math.round(Math.sin(a0) * pr);
    pb(px, floorY, pz, "red_terracotta");
    pb(px, floorY + 1, pz, "gold_block");
    ps(px, deckY, pz, "polished_andesite_slab[type=bottom]");
}
// deck edge kerb + gold studs
for (x = -R_CANOPY; x <= R_CANOPY; x++) {
    for (z = -R_CANOPY; z <= R_CANOPY; z++) {
        dd = Math.sqrt(x * x + z * z);
        t = ang(x, z);
        rr = polyR(t, R_DECK, 12);
        if (dd <= rr && dd > rr - 1.0) {
            ps(x, deckY + 1, z, "dark_oak_slab[type=bottom]");
            if ((x + z) % 5 === 0) pb(x, deckY + 1, z, "waxed_cut_copper_slab[type=bottom]");
        }
    }
}
// entry steps, south
var st;
for (st = 0; st < 3; st++) {
    var sz = R_DECK + 1 + st;
    var sxx;
    for (sxx = -3; sxx <= 3; sxx++) {
        roofStair(sxx, deckY - st, sz, "polished_andesite_stairs", "south");
        if (st === 2) pb(sxx, deckY - st - 1, sz, "polished_andesite");
    }
}
pb(-4, deckY, R_DECK + 1, "gold_block"); pb(4, deckY, R_DECK + 1, "gold_block");
pb(-4, deckY + 1, R_DECK + 1, "lantern"); pb(4, deckY + 1, R_DECK + 1, "lantern");

// ---------- MIRRORED CENTRE COLUMN ----------
makeCylinder(0, walkY, 0, "sea_lantern", 1, rimY - walkY, false);
var colMat = noisePalette([{ block: "quartz_block", weight: 40 }, { block: "smooth_quartz", weight: 25 }, { block: "white_stained_glass", weight: 25 }, { block: "gold_block", weight: 10 }], { seed: 21, freq: 0.3 });
makeCylinder(0, walkY, 0, colMat, 2.4, rimY - walkY, true);
// gold banding on the column
var cy;
for (cy = walkY + 2; cy < rimY; cy += 4) makeCylinder(0, cy, 0, "gold_block", 2.6, 1, true);
makeCylinder(0, walkY - 1, 0, "waxed_cut_copper", 3.4, 1, true);
makeCylinder(0, rimY - 1, 0, "gold_block", 3.2, 1, true);

// ---------- 12 RIM RODS carrying the canopy ----------
var ri;
for (ri = 0; ri < 12; ri++) {
    var ra = (Math.PI * 2 / 12) * ri + (Math.PI / 12);
    var rrad = polyR(ra, R_DECK - 1, 12);
    var rx = Math.round(Math.cos(ra) * rrad), rz = Math.round(Math.sin(ra) * rrad);
    var yy;
    for (yy = walkY; yy <= rimY - 1; yy++) pb(rx, yy, rz, "chain[axis=y]");
    pb(rx, walkY - 1, rz, "waxed_cut_copper");
    pb(rx, rimY - 1, rz, "gold_block");
}

// ---------- SCALLOPED VALANCE ----------
for (x = -R_CANOPY - 1; x <= R_CANOPY + 1; x++) {
    for (z = -R_CANOPY - 1; z <= R_CANOPY + 1; z++) {
        dd = Math.sqrt(x * x + z * z);
        t = ang(x, z);
        rr = polyR(t, R_CANOPY, 12);
        if (dd <= rr && dd > rr - 1.2) {
            var sector = Math.floor(t / (Math.PI * 2 / 12));
            pb(x, rimY, z, (sector % 2 === 0) ? "bone_block[axis=y]" : "red_terracotta");
            // scallop: upside-down stair skirt hanging one below the rim
            if (Math.abs(x) >= Math.abs(z)) roofStair(x, rimY - 1, z, (sector % 2 === 0) ? "smooth_quartz_stairs" : "red_sandstone_stairs", x > 0 ? "east" : "west");
            else roofStair(x, rimY - 1, z, (sector % 2 === 0) ? "smooth_quartz_stairs" : "red_sandstone_stairs", z > 0 ? "south" : "north");
            if ((x + z) % 4 === 0) { pb(x, rimY - 2, z, "lantern[hanging=true]"); }
        }
    }
}

// ---------- STRIPED 12-SECTOR CONICAL CANOPY ----------
var ki;
for (ki = 0; ki <= capH; ki++) {
    var y = rimY + 1 + ki;
    var shrink = 1 - (ki + 1) / (capH + 1.5);
    var Rl = R_CANOPY * shrink;
    if (Rl < 1.1) Rl = 1.1;
    var RR = Math.ceil(Rl) + 1;
    for (x = -RR; x <= RR; x++) {
        for (z = -RR; z <= RR; z++) {
            dd = Math.sqrt(x * x + z * z);
            t = ang(x, z);
            rr = polyR(t, Rl, 12);
            if (dd <= rr && dd > rr - 2.4) {
                var sec = Math.floor(t / (Math.PI * 2 / 12));
                var block;
                if (sec % 2 === 0) block = (((x + z + ki) % 7) === 0) ? "smooth_quartz" : (((x * 3 + z) % 5) === 0 ? "white_terracotta" : "bone_block[axis=y]");
                else block = (((x + z + ki) % 6) === 0) ? "red_concrete" : (((x + z * 2) % 5) === 0 ? "terracotta" : "red_terracotta");
                pb(x, y, z, block);
            }
        }
    }
}
// gold rib on every sector seam
var gi;
for (gi = 0; gi < 12; gi++) {
    var ga = (Math.PI * 2 / 12) * gi;
    for (ki = 0; ki <= capH; ki++) {
        var yg = rimY + 1 + ki;
        var shr = 1 - (ki + 1) / (capH + 1.5);
        var Rg = R_CANOPY * shr;
        if (Rg < 1.1) Rg = 1.1;
        var rg = polyR(ga, Rg, 12);
        pb(Math.round(Math.cos(ga) * rg), yg, Math.round(Math.sin(ga) * rg), "gold_block");
    }
}
// crown + flag
fill(-1, rimY + capH + 1, -1, 1, rimY + capH + 1, 1, "gold_block");
pb(0, rimY + capH + 2, 0, "gold_block");
pb(0, rimY + capH + 3, 0, "chain[axis=y]");
pb(0, rimY + capH + 4, 0, "chain[axis=y]");
ps(1, rimY + capH + 4, 0, "red_wall_banner[facing=east]");
pb(0, rimY + capH + 5, 0, "end_rod[facing=up]");

// ---------- HORSES ON BRASS POLES ----------
var horseBody = noisePalette([{ block: "bone_block[axis=y]", weight: 48 }, { block: "white_terracotta", weight: 30 }, { block: "smooth_quartz", weight: 22 }], { seed: 77, freq: 0.35 });
function carouselHorse(hx, hz, dx, dz, saddle) {
    var bodyY = walkY + 3;
    // barrel of the body
    drawLine(hx - dx * 2, bodyY, hz - dz * 2, hx + dx * 2, bodyY, hz + dz * 2, "bone_block[axis=y]", 1.6);
    // neck rising forward + head
    drawLine(hx + dx * 1.6, bodyY, hz + dz * 1.6, hx + dx * 3, bodyY + 2, hz + dz * 3, "bone_block[axis=y]", 1.1);
    // dapple the barrel so the horse is not one flat block
    var qi, qx, qz;
    for (qi = -2; qi <= 2; qi++) {
        qx = Math.round(hx + dx * qi); qz = Math.round(hz + dz * qi);
        if ((qi + 3) % 2 === 0) pb(qx, bodyY, qz, horseBody(qx, bodyY, qz));
        pb(Math.round(qx - dz * 1), bodyY, Math.round(qz + dx * 1), horseBody(qx, bodyY, qz));
    }
    pb(Math.round(hx + dx * 3.6), bodyY + 2, Math.round(hz + dz * 3.6), "bone_block[axis=y]");
    pb(Math.round(hx + dx * 4.2), bodyY + 2, Math.round(hz + dz * 4.2), "white_terracotta");
    pb(Math.round(hx + dx * 3), bodyY + 3, Math.round(hz + dz * 3), "gold_block");        // ear/plume
    // gold mane down the neck
    pb(Math.round(hx + dx * 2.2), bodyY + 2, Math.round(hz + dz * 2.2), "gold_block");
    pb(Math.round(hx + dx * 1.4), bodyY + 1, Math.round(hz + dz * 1.4), "gold_block");
    // legs down to the deck
    var px = -dz, pz = dx;
    var li, lj;
    for (li = -1; li <= 1; li += 2) {
        for (lj = -1; lj <= 1; lj += 2) {
            var lx = Math.round(hx + dx * 1.5 * li + px * 0.9 * lj);
            var lz = Math.round(hz + dz * 1.5 * li + pz * 0.9 * lj);
            var yy2;
            for (yy2 = walkY; yy2 < bodyY; yy2++) pb(lx, yy2, lz, "white_terracotta");
        }
    }
    // tail
    pb(Math.round(hx - dx * 2.6), bodyY, Math.round(hz - dz * 2.6), "gold_block");
    pb(Math.round(hx - dx * 2.9), bodyY - 1, Math.round(hz - dz * 2.9), "gold_block");
    // saddle + bridle
    pb(hx, bodyY + 1, hz, saddle);
    pb(Math.round(hx - dx * 0.8), bodyY + 1, Math.round(hz - dz * 0.8), saddle);
    // brass pole through the saddle up to the canopy
    var yy3;
    for (yy3 = bodyY + 2; yy3 <= rimY - 1; yy3++) pb(hx, yy3, hz, "chain[axis=y]");
    pb(hx, rimY - 1, hz, "gold_block");
}
var mounts = [
    [0, -9, 1, 0], [0, 9, -1, 0], [9, 0, 0, 1], [-9, 0, 0, -1],
    [6.4, -6.4, 0.7071, 0.7071], [-6.4, 6.4, -0.7071, -0.7071],
    [6.4, 6.4, -0.7071, 0.7071], [-6.4, -6.4, 0.7071, -0.7071]
];
var mi;
var saddles = ["red_wool", "blue_wool", "green_wool", "purple_wool", "red_wool", "orange_wool", "blue_wool", "magenta_wool"];
for (mi = 0; mi < mounts.length; mi++) {
    carouselHorse(Math.round(mounts[mi][0]), Math.round(mounts[mi][1]), mounts[mi][2], mounts[mi][3], saddles[mi]);
}
// two chariot benches between mounts (classic carousel seating)
function chariot(bx, bz, faceDir) {
    fill(bx - 1, walkY, bz - 1, bx + 1, walkY, bz + 1, "dark_oak_planks");
    chairStair(bx - 1, walkY + 1, bz, "spruce_stairs", faceDir);
    chairStair(bx, walkY + 1, bz, "spruce_stairs", faceDir);
    chairStair(bx + 1, walkY + 1, bz, "spruce_stairs", faceDir);
    pb(bx - 2, walkY + 1, bz, "gold_block"); pb(bx + 2, walkY + 1, bz, "gold_block");
    pb(bx - 2, walkY + 2, bz, "red_terracotta"); pb(bx + 2, walkY + 2, bz, "red_terracotta");
}
chariot(0, -12, "north");
chariot(0, 11, "south");

// ---------- PARK SURROUNDINGS ----------
var pathMat = noisePalette([{ block: "gravel", weight: 38 }, { block: "cobblestone", weight: 26 }, { block: "andesite", weight: 22 }, { block: "coarse_dirt", weight: 14 }], { seed: 44, freq: 0.25 });
var pz4;
for (pz4 = R_DECK + 4; pz4 <= 30; pz4++) {
    var pw = (pz4 > 22) ? 3 : 2;
    var pxx;
    for (pxx = -pw; pxx <= pw; pxx++) pb(pxx, floorY, pz4, pathMat(pxx, floorY, pz4));
}
// ring path around the carousel
for (x = -22; x <= 22; x++) {
    for (z = -22; z <= 22; z++) {
        dd = Math.sqrt(x * x + z * z);
        if (dd <= 18.5 && dd > 16.0) pb(x, floorY, z, pathMat(x, floorY, z));
    }
}
// park benches + lamp posts around the ring
function bench(bx, bz, dir) {
    pb(bx - 1, floorY + 1, bz, "dark_oak_fence");
    chairStair(bx, floorY + 1, bz, "spruce_stairs", dir);
    chairStair(bx + 1, floorY + 1, bz, "spruce_stairs", dir);
    pb(bx + 2, floorY + 1, bz, "dark_oak_fence");
}
bench(-19, 6, "east"); bench(16, -8, "west"); bench(-8, -19, "south"); bench(7, 18, "north");
function lamp(lx, lz) {
    pb(lx, floorY + 1, lz, "waxed_cut_copper");
    pb(lx, floorY + 2, lz, "dark_oak_fence");
    pb(lx, floorY + 3, lz, "dark_oak_fence");
    pb(lx, floorY + 4, lz, "dark_oak_fence");
    ps(lx + 1, floorY + 4, lz, "dark_oak_fence");
    pb(lx + 1, floorY + 3, lz, "lantern[hanging=true]");
    pb(lx, floorY + 5, lz, "gold_block");
}
lamp(-20, -4); lamp(20, 4); lamp(-5, 20); lamp(5, -20);
// striped ticket kiosk
var kx = -17, kz = 14;
fill(kx, floorY + 1, kz, kx + 3, floorY + 4, kz + 3, "red_terracotta");
fill(kx + 1, floorY + 1, kz + 1, kx + 2, floorY + 4, kz + 2, "air");
var kk;
for (kk = 0; kk <= 3; kk++) {
    pb(kx + kk, floorY + 2, kz, (kk % 2 === 0) ? "bone_block[axis=y]" : "red_terracotta");
    pb(kx + kk, floorY + 3, kz, (kk % 2 === 0) ? "red_terracotta" : "bone_block[axis=y]");
}
fill(kx + 1, floorY + 2, kz, kx + 2, floorY + 3, kz, "air");
ps(kx + 1, floorY + 2, kz, "dark_oak_trapdoor[facing=north,half=top,open=false]");
pb(kx + 1, floorY + 1, kz, "dark_oak_slab[type=top]"); pb(kx + 2, floorY + 1, kz, "dark_oak_slab[type=top]");
pb(kx + 2, floorY + 1, kz + 2, "barrel[facing=up]");
pb(kx + 1, floorY + 3, kz + 2, "lantern[hanging=true]");
var kr;
for (kr = -1; kr <= 4; kr++) {
    roofStair(kx + kr, floorY + 5, kz - 1, "red_sandstone_stairs", "north");
    roofStair(kx + kr, floorY + 5, kz + 4, "red_sandstone_stairs", "south");
    pb(kx + kr, floorY + 6, kz + 1, "smooth_quartz_slab[type=bottom]");
    pb(kx + kr, floorY + 6, kz + 2, "smooth_quartz_slab[type=bottom]");
}
ps(kx + 1, floorY + 4, kz - 1, "oak_wall_sign[facing=north]");
// trees + flowerbeds
function tree(tx, tz, th, sd) {
    var i3;
    for (i3 = 0; i3 < th; i3++) pb(tx, floorY + 1 + i3, tz, "oak_log[axis=y]");
    makeSphere(tx, floorY + th, tz, noisePalette([{ block: "oak_leaves[persistent=true]", weight: 60 }, { block: "birch_leaves[persistent=true]", weight: 22 }, { block: "azalea_leaves[persistent=true]", weight: 18 }], { seed: sd, freq: 0.3 }), 3.4, false);
    pb(tx, floorY + th + 3, tz, "oak_leaves[persistent=true]");
}
tree(-24, -12, 5, 2); tree(23, -16, 5, 8); tree(-22, 20, 4, 14); tree(24, 17, 5, 19); tree(2, -26, 5, 23);
// flower beds in the four quadrants of the ring
var fb2;
for (fb2 = 0; fb2 < 4; fb2++) {
    var fa = Math.PI / 4 + (Math.PI / 2) * fb2;
    var fcx = Math.round(Math.cos(fa) * 21), fcz = Math.round(Math.sin(fa) * 21);
    var ax2, az2;
    for (ax2 = -2; ax2 <= 2; ax2++) {
        for (az2 = -2; az2 <= 2; az2++) {
            if (Math.abs(ax2) + Math.abs(az2) > 3) continue;
            pb(fcx + ax2, floorY, fcz + az2, "rooted_dirt");
            pb(fcx + ax2, floorY + 1, fcz + az2, ((ax2 + az2) % 3 === 0) ? "red_tulip" : (((ax2 + az2) % 3 === 1) ? "orange_tulip" : "oxeye_daisy"));
        }
    }
    var bd;
    for (bd = -3; bd <= 3; bd++) {
        pb(fcx + bd, floorY, fcz - 3, "polished_andesite");
        pb(fcx + bd, floorY, fcz + 3, "polished_andesite");
    }
}
// scattered park grass and flowers
var fi;
for (fi = 0; fi < 110; fi++) {
    var fx = Math.floor(noise2d(fi * 2.3, 1.1) * 58) - 29;
    var fz = Math.floor(noise2d(0.7, fi * 3.7) * 58) - 29;
    var dcen = Math.sqrt(fx * fx + fz * fz);
    if (dcen < 19) continue;
    pb(fx, floorY + 1, fz, (fi % 4 === 0) ? "short_grass" : (fi % 4 === 1 ? "dandelion" : (fi % 4 === 2 ? "cornflower" : "short_grass")));
}

Static Renders

Vintage fairground carousel (guided) isometric viewisometric
Vintage fairground carousel (guided) front viewfront
Vintage fairground carousel (guided) side viewside
Vintage fairground carousel (guided) back viewback
Vintage fairground carousel (guided) top viewtop