← Basics3D assetfogon/biplano
Biplano
Drag to turn it// «Biplano» · gpt-6-sol (medium)
// Biplane («Biplano»). Be faithful to the reference: keep thin parts thin (1–2 blocks: strings, necks, handles, legs, rods), keep its exact proportions and silhouette, and every visible detail (books on shelves, sound holes, tuning pegs, valves). Don't make it chunkier than the reference.
// Biplane: nose faces -y, with a wide upper wing and a shorter lower wing.
const yellow = "#efb92e", gold = "#d99a20", light = "#ffd85b";
const navy = "#193b49", teal = "#275566", shadow = "#122d39";
const steel = "#87979a", darkSteel = "#53676d";
function wing(x0, x1, y0, y1, z, upper) {
// Thin, slightly rounded tips.
for (let x = x0; x <= x1; x++) {
const tip = Math.min(x - x0, x1 - x);
for (let y = y0 + (tip === 0 ? 1 : 0); y <= y1 - (tip === 0 ? 1 : 0); y++) {
set(x, y, z, y === y0 ? gold : yellow);
set(x, y, z + 1, y === y1 ? "#e5a92a" : light);
if (tip > 1 && (x + 2 * y) % 17 === 0)
set(x, y, z + 1, "#f6c943");
}
}
// Fine wing ribs, rather than thick bands.
for (let x = x0 + 4; x < x1 - 2; x += 5)
for (let y = y0 + 1; y <= y1 - 1; y++)
set(x, y, z + 1, upper ? "#f6c44a" : "#f4c13f");
for (let x = x0 + 1; x < x1; x++) {
set(x, y0, z, "#b98120");
set(x, y1, z, "#d49924");
}
}
function roundel(cx, cy) {
for (let dx = -3; dx <= 3; dx++)
for (let dy = -3; dy <= 3; dy++) {
const d = Math.hypot(dx, dy);
if (d <= 3.2)
set(cx + dx, cy + dy, 36,
d < 1.05 ? "#b93830" : d < 2.05 ? "#fff0ca" : "#225b72");
}
}
// Slim, tapering fuselage and tail boom.
for (let y = 12; y <= 41; y++) {
const half = y < 24 ? 3 : y < 32 ? 2 : 1;
const bottom = y < 29 ? 21 : 22;
const top = y < 27 ? 26 : 24;
box(24 - half, y, bottom, 24 + half, y, top, y < 29 ? teal : navy);
for (let x = 24 - half; x <= 24 + half; x++)
set(x, y, top, y < 29 ? "#376d73" : "#285063");
}
line(24, 27, 23, 24, 41, 23, 0, "#183845");
box(23, 39, 21, 25, 42, 22, shadow);
// Tailplane, thin yellow elevators, and the tall striped fin.
wing(13, 35, 36, 40, 21, false);
box(16, 40, 21, 32, 41, 21, gold);
for (let y = 35; y <= 41; y++)
for (let z = 24; z <= 35 - Math.max(0, y - 37); z++)
set(24, y, z, z > 31 ? yellow : navy);
box(24, 36, 35, 24, 38, 37, gold);
box(24, 37, 38, 24, 39, 38, light);
set(24, 39, 39, "#bd8321");
// Lower wing remains thin and clearly separated from the upper wing.
wing(9, 39, 16, 23, 22, false);
box(12, 16, 22, 37, 16, 22, "#b8791c");
// Dark cockpit opening with a narrow golden rim.
box(21, 17, 26, 27, 25, 27, "#d69a25");
box(22, 19, 28, 26, 23, 28, "#dca632");
box(23, 20, 28, 25, 23, 28, "#122b32");
box(23, 20, 29, 25, 21, 29, "#183740");
set(22, 18, 29, "#a9c4bb");
set(26, 18, 29, "#a9c4bb");
box(21, 15, 26, 27, 17, 27, light);
for (let x = 22; x <= 26; x++) set(x, 15, 28, gold);
// Exposed engine, silver nose and a few separate cylinder details.
cylinder(24, 9, 23, 4, 6, darkSteel, "y");
for (let x = 21; x <= 27; x++) {
set(x, 10, 26, steel);
if (x % 2 === 0) set(x, 12, 27, "#afae91");
}
box(20, 11, 22, 21, 15, 24, shadow);
box(27, 11, 22, 28, 15, 24, shadow);
for (const x of [21, 23, 25, 27])
box(x, 8, 24, x, 12, 25, "#a0a9a2");
box(22, 7, 21, 26, 9, 25, "#71878a");
box(23, 6, 22, 25, 7, 24, "#b3b8aa");
// Two wooden propeller blades, kept narrow.
line(24, 5, 23, 27, 5, 31, 1, "#8b4d24");
line(24, 5, 23, 21, 5, 16, 1, "#935126");
line(26, 5, 28, 28, 5, 32, 0, "#bc7840");
line(22, 5, 18, 20, 5, 15, 0, "#bd793c");
box(23, 4, 22, 25, 5, 24, "#9aa3a0");
set(24, 3, 23, "#d1cbb3");
// Thin interplane struts and crossing bracing wires.
for (const x of [12, 36]) {
for (const y of [18, 22])
box(x, y, 24, x, y, 33, navy);
line(x, 18, 25, x, 22, 32, 0, "#5a8085");
line(x, 22, 25, x, 18, 32, 0, "#587b80");
}
for (const x of [20, 29]) {
box(x, 20, 26, x, 20, 33, shadow);
box(x, 23, 27, x, 23, 33, teal);
}
line(13, 20, 26, 19, 20, 33, 0, "#678887");
line(35, 20, 26, 29, 20, 33, 0, "#678887");
// Broad-span top wing, its ribbing and two prominent insignia.
wing(3, 44, 16, 23, 34, true);
roundel(10, 20);
roundel(38, 20);
for (let x = 5; x <= 42; x++)
set(x, 16, 34, "#bf821c");
// Landing-gear struts, axle and two small, upright rubber wheels.
for (const x of [15, 33]) {
const root = x < 24 ? 21 : 27;
line(root, 13, 21, x, 14, 6, 0, shadow);
line(root, 20, 21, x, 14, 7, 0, teal);
line(x, 14, 7, 24, 15, 11, 0, "#536a6e");
}
line(15, 14, 5, 33, 14, 5, 0, "#798c8c");
for (const wx of [15, 33]) {
for (let dx = -1; dx <= 1; dx++)
for (let dy = -4; dy <= 4; dy++)
for (let dz = -4; dz <= 4; dz++) {
const d = Math.hypot(dy, dz);
if (d <= 4.15) {
const outer = d > 2.8 || (Math.abs(dx) === 1 && d > 2.2);
set(wx + dx, 14 + dy, 4 + dz,
outer ? (dz > 1 ? "#202c2e" : "#151d20") : "#344346");
}
}
for (const face of [-2, 2]) {
set(wx + face, 14, 4, "#8b9995");
set(wx + face, 13, 4, "#607477");
set(wx + face, 14, 5, "#738987");
}
}import { draw } from "./assets/voxel";
import { biplano } from "./assets/biplano";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, biplano, 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 biplano
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["biplano"] })
# It lands in client/assets/biplano.ts
# https://fogon.page/object/biplano