← Whole house3D assetfogon/banera
Bañera
Drag to turn it// «Bañera» · gpt-6-sol (medium)
// A classic clawfoot bathtub: a long white oval tub on four little golden claw feet, filled with light blue water and a pile of white soap foam bubbles, a golden faucet at one end and a yellow rubber duck floating in the water.
const white = "#f8f7f1";
const ivory = "#eeede6";
const shadow = "#d8dce0";
const gold = "#d79b37";
const goldLight = "#f5c963";
const goldDark = "#a96d27";
const water = "#a9deeb";
const cx = 24, cy = 24;
// Four little golden claw feet.
for (const x of [10, 38]) {
for (const y of [16, 32]) {
ellipsoid(x, y, 3, 2.5, 2.3, 3, gold);
ellipsoid(x, y, 5, 1.7, 1.7, 2, goldLight);
ellipsoid(x, y - 0.8, 1, 2.5, 2.3, 1.3, goldDark);
for (const dx of [-1.5, 0, 1.5]) {
ellipsoid(x + dx, y - 2, 0.8, 0.8, 1.4, 0.8, goldLight);
}
}
}
// Solid, gently tapered oval bath, with an open interior.
for (let z = 6; z <= 22; z++) {
let rx, ry;
if (z < 11) {
rx = 16 + (z - 6) * 0.6;
ry = 8.5 + (z - 6) * 0.55;
} else if (z < 19) {
rx = 19 + (z - 11) * 0.16;
ry = 11.2 + (z - 11) * 0.12;
} else {
rx = 20.5 + (z - 19) * 0.16;
ry = 12.1 + (z - 19) * 0.14;
}
const innerRx = z < 11 ? 0 : 14.7 + (z - 11) * 0.38;
const innerRy = z < 11 ? 0 : 7.4 + (z - 11) * 0.27;
for (let x = 2; x <= 46; x++) for (let y = 10; y <= 38; y++) {
const outer = ((x - cx) / rx) ** 2 + ((y - cy) / ry) ** 2;
const inner = z < 11 ? 2 :
((x - cx) / innerRx) ** 2 + ((y - cy) / innerRy) ** 2;
if (outer <= 1 && inner >= 1) {
let color = y < 19 || z < 10 ? shadow : ivory;
if (z >= 20) color = white;
if (rand() < 0.045) color = color === white ? ivory : white;
set(x, y, z, color);
}
}
}
// Thick, rounded lip, with a fine pale-grey line beneath it.
for (let x = 2; x <= 46; x++) for (let y = 10; y <= 38; y++) {
const d = ((x - cx) / 21.3) ** 2 + ((y - cy) / 12.9) ** 2;
if (d > 0.88 && d <= 1) {
set(x, y, 21, shadow);
set(x, y, 22, white);
}
}
// Calm water inside the rim.
for (let x = 5; x <= 43; x++) for (let y = 13; y <= 35; y++) {
if (((x - cx) / 17.7) ** 2 + ((y - cy) / 9.8) ** 2 < 1) {
const ripple = rand();
set(x, y, 18, ripple < 0.14 ? "#c6ecf3" : water);
set(x, y, 19, ripple < 0.18 ? "#dcf5f6" :
ripple < 0.48 ? "#bde9f0" : water);
}
}
// A few curved-looking glints across the water.
for (let x = 13; x <= 20; x++) set(x, 28 + Math.round(Math.sin(x) * 0.7), 20, "#e4f8f6");
for (let x = 30; x <= 35; x++) set(x, 29 + Math.round(Math.sin(x) * 0.7), 20, "#d8f4f5");
// Frothy mound at the left end, with separate round bubbles.
const bubbles = [
[13, 22, 20, 2.3], [17, 20, 20, 2.8], [16, 25, 20, 2.5],
[20, 23, 20, 2.1], [14, 25, 22, 1.9], [17, 22, 23, 2.2],
[20, 20, 22, 1.7], [12, 20, 22, 1.3], [18, 26, 23, 1.4],
[15, 22, 25, 1.3], [20, 24, 22, 1.2]
];
for (let i = 0; i < bubbles.length; i++) {
const [x, y, z, r] = bubbles[i];
sphere(x, y, z, r, i % 3 === 0 ? "#e3edf0" : "#fffefa");
set(x - 1, y - 1, Math.round(z + r - 0.4), "#ffffff");
}
for (const [x, y, z] of [[22, 18, 20], [11, 27, 20], [21, 28, 20], [25, 27, 20]]) {
sphere(x, y, z, 1, "#ffffff");
}
// Golden faucet mounted on the right-hand end, bent over the water.
cylinder(43, 24, 22, 1.6, 7, gold, "z");
cylinder(43, 24, 28, 2, 1, goldLight, "z");
line(43, 24, 28, 36, 24, 28, 1.3, goldLight);
ellipsoid(36, 24, 27, 1.6, 1.6, 1.5, gold);
cylinder(36, 24, 26, 1, 2, goldDark, "z");
for (const y of [20, 28]) {
sphere(43, y, 23, 1.4, goldLight);
line(41, y, 24, 45, y, 24, 0.7, gold);
sphere(41, y, 24, 0.8, goldLight);
sphere(45, y, 24, 0.8, goldLight);
}
// A little rubber duck floating at the front-right of the bath.
part("duck", [28, 21, 20]);
ellipsoid(28, 22, 21, 4, 3, 2.6, "#f6c941");
ellipsoid(28, 21, 22, 3.5, 2.4, 1.7, "#ffdf5b");
ellipsoid(28, 19, 24, 2.2, 2.1, 2.5, "#ffda4a");
sphere(28, 19, 26, 1.7, "#ffe46d");
ellipsoid(28, 16.7, 23.7, 1.7, 1.4, 0.8, "#f28b43");
box(27, 15, 23, 29, 16, 23, "#e87536");
for (const x of [26, 30]) {
set(x, 17, 25, "#30373b");
set(x, 16, 25, "#30373b");
set(x, 16, 26, "#ffffff");
}
ellipsoid(25, 22, 22, 1.3, 1.6, 0.8, "#f3bd39");
ellipsoid(31, 22, 22, 1.3, 1.6, 0.8, "#f3bd39");
ellipsoid(28, 25, 22, 1.5, 1.3, 1, "#ffe46d");
animate("duck", { type: "bob", axis: "z", amplitude: 1, speed: 2 });import { draw } from "./assets/voxel";
import { banera } from "./assets/banera";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, banera, 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 banera
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["banera"] })
# It lands in client/assets/banera.ts
# https://fogon.page/object/banera