← Fantasy3D assetfogon/arbol-de-cristal
Árbol de cristal
Drag to turn it// «Árbol de cristal» · gpt-6-sol (medium)
// A magical tree made of crystal: a twisting trunk and branches of pale violet crystal, with clusters of glowing cyan and pink crystal shards instead of leaves, growing from a small mound of dark stone.
// A crystal tree rooted in a mossy stone mound.
const stone = ["#303841", "#3c4650", "#505a62", "#667078"];
const moss = ["#3c6258", "#4e806d", "#70a382"];
const violet = ["#674781", "#8964a5", "#b191cf", "#ddc5ef"];
const glow = ["#40e7ea", "#ff79cd", "#a985f4", "#ecffff"];
// Rounded, uneven stone mound.
ellipsoid(24, 24, 2, 17, 12, 4, stone[1]);
ellipsoid(22, 23, 2, 13, 10, 4, stone[2]);
for (let i = 0; i < 32; i++) {
const a = rand() * Math.PI * 2;
const d = 10 + rand() * 7;
sphere(24 + Math.cos(a) * d, 24 + Math.sin(a) * d * 0.72,
1 + rand() * 1.5, 1 + rand() * 1.6, stone[Math.floor(rand() * 4)]);
}
// Moss follows the upper surface rather than covering the stone completely.
for (let x = 9; x <= 39; x++) {
for (let y = 14; y <= 34; y++) {
if (rand() > 0.54) continue;
for (let z = 6; z >= 2; z--) {
if (get(x, y, z) && !get(x, y, z + 1)) {
set(x, y, z + 1, moss[Math.floor(rand() * moss.length)]);
break;
}
}
}
}
// Small storybook mushrooms on the front of the mound.
for (const [x, y, z, cap] of [
[12, 19, 3, "#f49aba"], [35, 20, 3, "#e69a65"],
[17, 14, 3, "#ba8be2"], [32, 15, 4, "#f49aba"]
]) {
box(x, y, z, x, y, z + 2, "#e8d8bb");
ellipsoid(x, y, z + 3, 2, 2, 1, cap);
set(x - 1, y - 1, z + 3, "#fff0d7");
}
// Thick, gently twisting quartz trunk.
let previous = [24, 24, 5];
for (let z = 6; z <= 32; z += 2) {
const x = 24 + Math.sin(z * 0.31) * 2.4;
const y = 24 + Math.cos(z * 0.27) * 1.5;
const r = z < 13 ? 4 : z < 24 ? 3 : 2;
line(...previous, x, y, z, r, violet[0]);
line(previous[0] - 1, previous[1] - 1, previous[2],
x - 1, y - 1, z, Math.max(1, r - 1), violet[2]);
previous = [x, y, z];
}
// Long pale facets on the front face of the twisting trunk.
line(22, 20, 7, 24, 20, 17, 0, violet[3]);
line(25, 20, 13, 22, 21, 24, 0, "#efdafa");
line(23, 21, 25, 25, 22, 32, 0, violet[3]);
// Root claws spread over the mound.
for (const [x, y] of [[13, 20], [35, 20], [18, 32], [31, 32], [24, 13]]) {
line(24, 24, 7, x, y, 4, 2, violet[0]);
line(23, 23, 8, x, y - 1, 5, 1, violet[2]);
}
// Branches are solid crystal, visible through gaps in the glowing canopy.
const crowns = [
[10, 18, 30], [15, 13, 35], [23, 12, 36], [32, 14, 35],
[38, 20, 30], [37, 29, 31], [31, 35, 34], [21, 36, 34],
[12, 30, 29], [17, 23, 37], [29, 24, 38], [24, 25, 35]
];
for (let i = 0; i < crowns.length; i++) {
const [x, y, z] = crowns[i];
const fromZ = i % 3 === 0 ? 21 : 25;
const midX = 24 + (x - 24) * 0.48;
const midY = 24 + (y - 24) * 0.48;
const midZ = fromZ + (z - fromZ) * 0.58;
line(24, 24, fromZ, midX, midY, midZ, 2, violet[0]);
line(midX, midY, midZ, x, y, z, 2, violet[1]);
line(midX, midY - 1, midZ + 1, x, y - 1, z, 1, violet[2]);
}
// A few exposed angular knots and twigs beneath the leaves.
for (const [x, y, z] of crowns) {
line(x, y, z - 3, x, y, z + 1, 1, violet[2]);
set(x, y - 2, z - 1, violet[3]);
}
// All luminous foliage belongs to one color-cycling part.
part("crystalLeaves", [24, 24, 28]);
function shard(x, y, z, dx, dy, dz, c, large = false) {
const w = large ? 2 : 1;
line(x, y, z, x + dx * 0.35, y + dy * 0.35, z + dz * 0.38, w, c);
line(x + dx * 0.35, y + dy * 0.35, z + dz * 0.38,
x + dx * 0.77, y + dy * 0.77, z + dz * 0.78, 1, c);
line(x + dx * 0.77, y + dy * 0.77, z + dz * 0.78,
x + dx, y + dy, z + dz, 0, "#f1ffff");
}
for (let i = 0; i < crowns.length; i++) {
const [x, y, z] = crowns[i];
const c = glow[i % glow.length];
ellipsoid(x, y, z, 2, 2, 2, c);
// A tall central point plus outward-fanning crystal leaves.
shard(x, y, z, (rand() - 0.5) * 2, (rand() - 0.5) * 2,
i === 11 ? 10 : 6 + rand() * 2, c, true);
for (let j = 0; j < 7; j++) {
const a = j * Math.PI * 2 / 7 + i * 0.35;
const reach = 3 + rand() * 2.5;
const height = 2 + rand() * 4;
shard(x + Math.cos(a) * 0.7, y + Math.sin(a) * 0.7, z,
Math.cos(a) * reach, Math.sin(a) * reach, height,
glow[(i + j) % glow.length], j % 3 === 0);
}
}
// Floating chips give the canopy a bright, sparkling outline.
for (let i = 0; i < 55; i++) {
const [x, y, z] = crowns[Math.floor(rand() * crowns.length)];
const ox = Math.round((rand() - 0.5) * 10);
const oy = Math.round((rand() - 0.5) * 9);
const oz = Math.round((rand() - 0.5) * 9);
if (Math.abs(ox) + Math.abs(oy) + Math.abs(oz) > 8) continue;
set(x + ox, y + oy, z + oz, glow[Math.floor(rand() * glow.length)]);
}
animate("crystalLeaves", {
type: "colors",
palette: ["#36f2ee", "#ff75cb", "#b48cff", "#f0ffff"],
pattern: "noise",
glow: 0.7
});import { draw } from "./assets/voxel";
import { arbolDeCristal } from "./assets/arbol-de-cristal";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, arbolDeCristal, 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 arbol-de-cristal
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["arbol-de-cristal"] })
# It lands in client/assets/arbol-de-cristal.ts
# https://fogon.page/object/arbol-de-cristal