← Animals3D assetfogon/ballena
Ballena
Drag to turn it// «Ballena» · gpt-6-sol (medium)
// A huge chubby cartoon blue whale, horizontal along y, head at the front (-y): a big rounded dark blue body with a pale blue belly with grooves, small eyes near the mouth, a wide smiling mouth, two flippers, a tiny dorsal fin and a big horizontal tail fluke. A small white water spout on top of its head.
// Big rounded body and a low, pale belly.
ellipsoid(24, 20, 14, 15, 15, 13, "#2769ad");
ellipsoid(24, 20, 10, 12, 12, 10, "#2769ad");
// Narrow the body into the tail joint.
ellipsoid(24, 32, 14, 7, 8, 6, "#2769ad");
ellipsoid(24, 37, 14, 3, 5, 3, "#2769ad");
// Color the body in broad, readable bands.
forEach((x, y, z, color) => {
if (color !== "#2769ad") return;
if (z <= (y < 17 ? 12 : 10) && y < 32) {
set(x, y, z, rand() < 0.07 ? "#91d3e9" : z < 5 ? "#77bedb" : "#a5ddef");
} else if (z >= 20) {
set(x, y, z, rand() < 0.09 ? "#3981c2" : "#3176b8");
} else {
set(x, y, z, rand() < 0.07 ? "#225d9f" : "#2769ad");
}
});
// Find the front-facing block at a given point on the face.
function front(x, z) {
for (let y = 3; y <= 32; y++) {
if (get(x, y, z)) return y;
}
return null;
}
// Fine grooves radiating down the pale chin.
for (const x of [17, 21, 25, 29, 33]) {
for (let z = 3; z <= 8; z++) {
const y = front(x, z);
if (y !== null && y < 23) set(x, y - 1, z, "#70b6d4");
}
}
// An exaggerated, upturned smile across the front of the head.
for (let x = 12; x <= 36; x++) {
const z = Math.round(9 + 0.035 * (x - 24) * (x - 24));
const y = front(x, z);
if (y !== null) {
set(x, y - 1, z, "#173e68");
if (x >= 19 && x <= 29) set(x, y - 1, z + 1, "#19446d");
}
}
// Small black dot eyes, with one bright toy-like glint each.
for (const x of [15, 33]) {
const z = 19;
const y = front(x, z);
ellipsoid(x, y - 1, z, 1.5, 1.3, 1.5, "#102541");
set(x - 1, y - 2, z + 1, "#ffffff");
}
// Broad, swept-back flippers.
for (const side of [-1, 1]) {
for (let i = 0; i <= 10; i++) {
const x = 24 + side * (12 + i);
const y = 20 + Math.round(i * 0.9);
const r = i < 6 ? 3 : 2;
ellipsoid(x, y, 10 - Math.round(i * 0.15), 2, r, 2,
i > 7 ? "#3179b9" : "#2464a7");
}
ellipsoid(24 + side * 22, 30, 8, 2, 2, 1, "#4c94c9");
}
// Tiny dorsal fin, peeking over the back.
for (let z = 25; z <= 30; z++) {
const width = Math.max(0, 3 - Math.floor((z - 25) / 2));
box(24 - width, 31 + Math.floor((z - 25) / 3), z,
24 + width, 34, z, z >= 28 ? "#347cbb" : "#245f9f");
}
// The fluke is one moving piece, hinged at the tail joint.
part("tail", [24, 37, 14]);
ellipsoid(24, 39, 14, 3, 4, 2, "#245f9f");
for (let dx = -14; dx <= 14; dx++) {
const a = Math.abs(dx);
const y0 = 38 + Math.floor(a * 0.36);
const y1 = Math.min(46, 42 + Math.floor(a * 0.32));
const halfThickness = a < 5 ? 2 : 1;
for (let y = y0; y <= y1; y++) {
for (let z = 14 - halfThickness; z <= 14 + halfThickness; z++) {
set(24 + dx, y, z,
z === 14 + halfThickness ? "#3984bd" : "#245f9f");
}
}
}
animate("tail", { type: "swing", axis: "x", speed: 2, amplitude: 0.3 });
part("body");
// A little fountain rising from the top of the head.
part("spout", [24, 12, 26]);
line(24, 12, 26, 24, 12, 32, 1, "#f4fcff");
line(24, 12, 32, 21, 11, 35, 0, "#f4fcff");
line(24, 12, 32, 27, 11, 35, 0, "#f4fcff");
line(24, 12, 32, 24, 15, 35, 0, "#f4fcff");
sphere(21, 11, 35, 1, "#f4fcff");
sphere(27, 11, 35, 1, "#f4fcff");
sphere(24, 15, 35, 1, "#f4fcff");
animate("spout", {
type: "colors",
palette: ["#ffffff", "#eafaff", "#bdeeff"],
pattern: "flicker",
glow: 0.25
});
part("body");import { draw } from "./assets/voxel";
import { ballena } from "./assets/ballena";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, ballena, 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 ballena
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["ballena"] })
# It lands in client/assets/ballena.ts
# https://fogon.page/object/ballena