Let's vibe code Christmas trees

Mike Croucher on 15 Dec 2025
Latest activity Reply by Seth DeLand on 16 Dec 2025

I thought it would be fun to vibe code Christmas trees.
Model: Claude Sonnet4.5
Prompt: Write a MATLAB script that draws a Christmas tree. There should be decorations and presents too.
Result:
christmasTree()
function christmasTree()
% Christmas Tree with Decorations and Presents
% Creates a festive scene with a decorated tree and gift boxes
figure('Color', 'w', 'Position', [100, 100, 800, 900]);
hold on;
axis equal;
axis off;
xlim([-3, 3]);
ylim([0, 10]);
% Draw the tree (three layered triangular sections)
drawTreeSection(0, 2, 2.5, 2.5, [0.1, 0.5, 0.1]);
drawTreeSection(0, 3.5, 2, 2, [0.15, 0.55, 0.15]);
drawTreeSection(0, 4.8, 1.5, 1.8, [0.2, 0.6, 0.2]);
% Tree trunk
rectangle('Position', [-0.3, 0, 0.6, 2], 'FaceColor', [0.4, 0.2, 0.1], ...
'EdgeColor', 'none');
% Add decorations
addOrnaments();
addTinsel();
% Star on top
drawStar(0, 7.2, 0.4, [1, 0.84, 0]);
% Add presents under the tree
drawPresent(-1.8, 0, 0.6, 0.5, [0.8, 0.1, 0.1], [1, 0.84, 0]);
drawPresent(-0.8, 0, 0.5, 0.6, [0.1, 0.3, 0.8], [0.9, 0.9, 0.9]);
drawPresent(1.2, 0, 0.7, 0.4, [0.7, 0.5, 0.9], [0.2, 0.8, 0.2]);
drawPresent(0.2, 0, 0.4, 0.5, [1, 0.7, 0.1], [0.8, 0.1, 0.1]);
% Add some snow on the ground
scatter(randn(1, 100)*2.5, rand(1, 100)*0.3, 20, 'w', 'filled', ...
'MarkerEdgeColor', [0.8, 0.8, 0.8]);
title('Merry Christmas!', 'FontSize', 24, 'Color', [0.8, 0.1, 0.1], ...
'FontWeight', 'bold');
hold off;
end
function drawTreeSection(x, y, width, height, color)
% Draw a triangular section of the tree
xPoints = [x - width, x + width, x];
yPoints = [y, y, y + height];
fill(xPoints, yPoints, color, 'EdgeColor', [0.05, 0.3, 0.05], ...
'LineWidth', 1.5);
end
function addOrnaments()
% Add colorful baubles to the tree
ornamentColors = [
1, 0, 0; % Red
0, 0, 1; % Blue
1, 0.84, 0; % Gold
0.9, 0.1, 0.5;% Pink
0.5, 0, 0.5 % Purple
];
% Ornament positions (x, y coordinates)
positions = [
-1.5, 2.5; 1.5, 2.5; 0, 3.2;
-1.2, 3.8; 1.2, 3.8; -0.6, 4.2; 0.6, 4.2;
-0.8, 5.2; 0.8, 5.2; 0, 5.8; -0.4, 6.3; 0.4, 6.3
];
for i = 1:size(positions, 1)
colorIdx = mod(i-1, size(ornamentColors, 1)) + 1;
x = positions(i, 1);
y = positions(i, 2);
% Draw bauble
theta = linspace(0, 2*pi, 50);
radius = 0.15;
xCirc = x + radius * cos(theta);
yCirc = y + radius * sin(theta);
fill(xCirc, yCirc, ornamentColors(colorIdx, :), ...
'EdgeColor', ornamentColors(colorIdx, :)*0.7, 'LineWidth', 1.5);
% Add highlight for shine
highlightX = x - radius*0.4 + radius*0.3*cos(theta(1:15));
highlightY = y + radius*0.4 + radius*0.3*sin(theta(1:15));
fill(highlightX, highlightY, [1, 1, 1], 'EdgeColor', 'none', ...
'FaceAlpha', 0.6);
end
end
function addTinsel()
% Add sparkly tinsel effect
tinselX = [-1.8, -1.2, -0.5, 0.2, 0.9, 1.6];
tinselY = [2.8, 3.5, 4.0, 4.5, 5.0, 5.5];
for i = 1:length(tinselX)
plot(tinselX(i) + randn(1, 5)*0.1, tinselY(i) + rand(1, 5)*0.2, ...
'*', 'Color', [1, 0.84, 0], 'MarkerSize', 8, 'LineWidth', 1.5);
end
end
function drawStar(x, y, size, color)
% Draw a 5-pointed star on top of the tree
angles = linspace(0, 2*pi, 11);
radii = repmat([size, size*0.4], 1, 5);
radii = [radii, radii(1)];
xStar = x + radii .* cos(angles - pi/2);
yStar = y + radii .* sin(angles - pi/2);
fill(xStar, yStar, color, 'EdgeColor', [0.8, 0.6, 0], 'LineWidth', 2);
% Add sparkle
plot(x, y, '*', 'Color', [1, 1, 1], 'MarkerSize', 15, 'LineWidth', 2);
end
function drawPresent(x, y, width, height, boxColor, ribbonColor)
% Draw a gift box with ribbon and bow
% Box
rectangle('Position', [x, y, width, height], 'FaceColor', boxColor, ...
'EdgeColor', boxColor*0.7, 'LineWidth', 2);
% Vertical ribbon
ribbonWidth = width * 0.15;
rectangle('Position', [x + width/2 - ribbonWidth/2, y, ribbonWidth, height], ...
'FaceColor', ribbonColor, 'EdgeColor', 'none');
% Horizontal ribbon
ribbonHeight = height * 0.15;
rectangle('Position', [x, y + height/2 - ribbonHeight/2, width, ribbonHeight], ...
'FaceColor', ribbonColor, 'EdgeColor', 'none');
% Bow on top
bowX = x + width/2;
bowY = y + height;
bowSize = width * 0.2;
% Left loop
theta = linspace(0, pi, 30);
fill(bowX - bowSize*0.3 + bowSize*0.5*cos(theta), ...
bowY + bowSize*0.5*sin(theta), ribbonColor, 'EdgeColor', 'none');
% Right loop
fill(bowX + bowSize*0.3 + bowSize*0.5*cos(theta), ...
bowY + bowSize*0.5*sin(theta), ribbonColor, 'EdgeColor', 'none');
% Center knot
theta = linspace(0, 2*pi, 30);
fill(bowX + bowSize*0.25*cos(theta), bowY + bowSize*0.25*sin(theta), ...
ribbonColor*0.8, 'EdgeColor', 'none');
end
Manavendra
Manavendra on 16 Dec 2025 (Edited on 16 Dec 2025)
Prompts and follow-up prompts in MATLAB Copilot:
"please write code to create an animated plot of a christmas tre"
"modify the code to create a three dimentional christmas tree"
"modify the code to create a 10 second animation and save it as a GIF"
[ Error encountered :( ]
Explain error
"please incorporate the recommendations to modify the above code"
Code:
% christmas_tree_3d_gif_validated.m
clf
rng(1)
fig = figure('Color',[0.02 0.02 0.07],'MenuBar','none','ToolBar','none');
axis equal off
hold on
view(40,18)
set(fig,'Renderer','opengl') % reliable for capturing
% Parameters
layers = 5;
baseRadius = 6;
layerHeight = 1.6;
layerShrink = 0.75;
trunkW = 1.2;
trunkH = 1.6;
% Create stacked cones for tree
for k = 1:layers
r = baseRadius * layerShrink^(k-1);
h = layerHeight;
z0 = (k-1)* (layerHeight*0.9);
[X,Y,Z] = cylinder([0 r], 50);
Z = Z * h + z0;
X = flipud(X); Y = flipud(Y); Z = flipud(Z);
surf(X, Y, Z, 'FaceColor', [0 0.55-0.07*k 0], 'EdgeColor','none','FaceLighting','gouraud');
end
% Trunk
[tx,ty,tz] = cylinder(trunkW/2,40);
tz = tz * (-trunkH) - 0.2;
surf(tx,ty,tz,'FaceColor',[0.45 0.25 0.06],'EdgeColor','none','FaceLighting','gouraud');
% Star
starZ = layers*(layerHeight*0.9) + 0.6;
[xs,ys,zs] = sphere(8);
hStar = surf(xs*0.35, ys*0.35, zs*0.35 + starZ, 'FaceColor','y', 'EdgeColor','none','FaceLighting','gouraud');
% Ornaments
nOrn = 40;
ornPos = zeros(nOrn,3);
ornColors = [1 0 0; 0 0.9 1; 1 0.6 0; 1 0 1; 0.9 0.9 0]; % must be Nx3
% Validate ornColors shape
assert(size(ornColors,2)==3, 'ornColors must have 3 columns (RGB).');
for i=1:nOrn
L = randi(layers);
r = baseRadius * layerShrink^(L-1);
h = layerHeight;
z0 = (L-1)* (layerHeight*0.9);
theta = 2*pi*rand;
t = rand;
rr = r * t; zz = z0 + t*h;
ornPos(i,:) = [rr*cos(theta), rr*sin(theta), zz];
end
ornRadius = 0.18;
hOrn = gobjects(nOrn,1);
[bx,by,bz] = sphere(10);
for i=1:nOrn
xc = ornPos(i,1); yc = ornPos(i,2); zc = ornPos(i,3);
hOrn(i) = surf(bx*ornRadius+xc, by*ornRadius+yc, bz*ornRadius+zc, ...
'FaceColor',ornColors(mod(i-1,size(ornColors,1))+1,:), 'EdgeColor','none','FaceLighting','gouraud');
end
blinkState = randi([0 1],nOrn,1);
% Snow
nSnow = 120;
xlimVal = baseRadius*1.2;
sx = (rand(nSnow,1)-0.5)*xlimVal*2;
sy = (rand(nSnow,1)-0.5)*xlimVal*2;
sz = rand(nSnow,1)*(starZ+2);
hSnow = scatter3(sx,sy,sz,12,'w','filled','MarkerFaceAlpha',0.8);
% Lighting and material
camlight headlight
light('Position',[ -1 1 2],'Style','infinite')
material shiny
xlim([-xlimVal xlimVal]); ylim([-xlimVal xlimVal]); zlim([-trunkH-1, starZ+2]);
% GIF settings
gifFilename = 'christmas_tree_3d_validated.gif';
fps = 25; % frames per second
duration = 10; % seconds
T = fps * duration; % total frames
delayTime = 1 / fps;
% Debug flag: set true to print offending colors/indices
debug = false;
% Helper: clamp and validate RGB (1x3)
clampRGB = @(c) max(0,min(1,reshape(c,1,3)));
% Pre-draw to stabilize lighting/limits
drawnow
% Animation loop with GIF capture
for t = 1:T
% Blink ornaments
toggle = rand(nOrn,1) < 0.12;
blinkState(toggle) = ~blinkState(toggle);
for i=1:nOrn
% ensure hOrn(i) is a valid surface handle
if ~isgraphics(hOrn(i),'surface')
if debug
fprintf('hOrn(%d) is not a surface (class: %s)\n', i, class(hOrn(i)));
end
continue
end
if blinkState(i)
baseCol = ornColors(mod(i-1,size(ornColors,1))+1,:);
mult = (0.9 + 0.1*rand); % scalar in ~[0.9,1.0]
col = baseCol * mult;
else
col = [0.12 0.12 0.12];
end
% Validate size and clamp into [0,1]
if ~isnumeric(col) || numel(col) ~= 3
if debug, fprintf('Bad color at ornament %d: non-numeric or wrong length\n', i); end
col = [0.12 0.12 0.12];
end
col = clampRGB(col);
if debug && (any(col<0) || any(col>1))
fprintf('Clamped color at i=%d -> [%g %g %g]\n', i, col(1), col(2), col(3));
end
set(hOrn(i),'FaceColor',col,'FaceAlpha',1);
end
% Star twinkle
starCol = [1 0.92 0.2] * (0.8 + 0.4*abs(sin(0.15*t)));
% Ensure 1x3 and clamp
if ~isnumeric(starCol) || numel(starCol)~=3
starCol = [1 0.92 0.2];
end
starCol = clampRGB(starCol);
set(hStar,'FaceColor',starCol);
% Snow fall
sz = sz - (0.02 + 0.03*rand(nSnow,1));
fallen = sz < -trunkH-1;
if any(fallen)
sz(fallen) = starZ + 1 + rand(sum(fallen),1)*1.2;
sx(fallen) = (rand(sum(fallen),1)-0.5)*xlimVal*2;
sy(fallen) = (rand(sum(fallen),1)-0.5)*xlimVal*2;
end
set(hSnow,'XData',sx,'YData',sy,'ZData',sz);
% gentle camera orbit
camorbit(0.4,0,'data')
drawnow limitrate
% Capture frame and write to GIF
frame = getframe(fig);
[imind,cm] = rgb2ind(frame2im(frame),256);
if t == 1
imwrite(imind,cm,gifFilename,'gif','LoopCount',Inf,'DelayTime',delayTime);
else
imwrite(imind,cm,gifFilename,'gif','WriteMode','append','DelayTime',delayTime);
end
end
disp(['Saved GIF: ' gifFilename])
Saved GIF: christmas_tree_3d_validated.gif
Manavendra
Manavendra on 16 Dec 2025
Had to compress the GIF because of size constraints, and hence the static in the GIF.
Mike Croucher
Mike Croucher on 16 Dec 2025
Very nice!
Vasilis Bellos
Vasilis Bellos on 16 Dec 2025 (Edited on 16 Dec 2025)
Here's a hybrid approach where I asked Claude Opus 4.5 to enhance my code from last year (based on the original Rotating Christmas Tree code of the amazing @Zhaoxu Liu / slandarer). The final product after a few iterations:
%% Enhanced MATLAB Christmas Tree
% Based on: https://uk.mathworks.com/matlabcentral/discussions/tips/878025-christmas-tree-for-matlaber-rotating-with-falling-snowflakes
% Enhancements: trunk, tinsel, presents, star twinkle, wind drift, ground plane, LED chase animation, dust motes
fig = figure('Units','normalized', 'Position',[.25,0,.5,1],...
'Color',[0,9,33]/255);
ax = axes('Parent',fig, 'Position',[0,-1/6,1,1+1/3],...
'XLim',[-6,6], 'YLim',[-6,6], 'ZLim',[-23, 1], 'DataAspectRatio', [1,1,1], 'NextPlot','add',...
'Projection','perspective', 'Color',[0,9,33]/255, 'XColor','none', 'YColor','none', 'ZColor','none');
%% Draw Tree Skirt / Carpet (soft white disc, slightly larger than tree)
groundZ = -18;
carpetTheta = linspace(0, 2*pi, 100);
carpetR = 5.8;
carpetX = carpetR * cos(carpetTheta);
carpetY = carpetR * sin(carpetTheta);
carpetZ = ones(size(carpetX)) * groundZ;
patch('XData', carpetX, 'YData', carpetY, 'ZData', carpetZ, ...
'FaceColor', [0.98, 0.99, 1.0], 'EdgeColor', 'none', ...
'FaceAlpha', 0.92, 'FaceLighting', 'none');
%% Draw Tree Trunk
[Tx, Ty, Tz] = cylinder([0.35 0.45], 20);
Tz = Tz * 4.5 + groundZ; % Taller trunk, pierces into bottom tree layer
trunkHdl = surf(Tx, Ty, Tz, 'FaceColor', [0.35, 0.18, 0.05], 'EdgeColor', 'none', ...
'FaceLighting', 'gouraud', 'AmbientStrength', 0.4);
%% Draw Christmas Tree Layers
[X, T] = meshgrid(.4:.15:1, 0:pi/150:2*pi);
XM = 1 + sin(8.*T) .* .05;
X = X .* XM;
R = X.^(3) .* (.5 + sin(8.*T) .* .02);
dF = @(R, T, X) surf(R.*cos(T), R.*sin(T), -X, 'EdgeColor','interp',...
'FaceColor', [20,107,58]./255, 'FaceAlpha',.8, 'LineWidth',1,'MeshStyle','Column',...
'FaceLighting','gouraud','AmbientStrength',.3,'DiffuseStrength',.5,'SpecularStrength',.5);
cmap = colormap(turbo);
% LED strips - store for animation
cmap = colormap(repmat([cmap; flip(cmap)], 3, 1));
cmapLen = size(cmap, 1);
% Star light
arrayfun(@(x)light('Style','local','Position',[0 0 -1.3],'Color',[255,223,153]./255), 1:4);
CList = [255,100,100; 150,255,100; 0,255,255]./255;
rng(1);
% Store surface handles for LED animation
surfaceHandles = gobjects(6, 1);
% Store RGB light scatter handles for twinkling
rgbLightHandles = {};
rgbLightColors = {};
for i = 1:6
tR = R .* (2 + i);
tT = T + i;
tX = X .* (2 + i) + i;
SFHdl = dF(tR, tT, tX);
surfaceHandles(i) = SFHdl;
% LED strip gradient
for j = 1:size(SFHdl.CData, 2)
SFHdl.CData(:, j) = linspace(1, cmapLen, size(SFHdl.CData, 1));
end
[~, ind] = sort(SFHdl.ZData(:));
ind = ind(1:8);
C = CList(randi([1, size(CList, 1)], [8, 1]), :);
% Tree lights symmetry
if i == 1
C(length(C)/2+1:end, :) = C(1:length(C)/2, :);
end
if mod(i, 2)
for j = 1:length(ind)/2
P = drawPinecone;
set(P, 'Matrix', makehgtform('Translate', [tR(ind(j)).*cos(tT(ind(j))), ...
tR(ind(j)).*sin(tT(ind(j))), -tX(ind(j))], 'Scale', 0.35));
end
% Glow scatter (large, transparent)
glowHdl = scatter3(tR(ind(length(ind)/2+1:end)).*cos(tT(ind(length(ind)/2+1:end)))*1.01, ...
tR(ind(length(ind)/2+1:end)).*sin(tT(ind(length(ind)/2+1:end)))*1.01, ...
-tX(ind(length(ind)/2+1:end)), 500, 'filled',...
'CData', C(length(ind)/2+1:end,:), 'MarkerEdgeColor','none', 'MarkerFaceAlpha',.3);
% Core scatter (small, solid)
coreHdl = scatter3(tR(ind(length(ind)/2+1:end)).*cos(tT(ind(length(ind)/2+1:end)))*1.01, ...
tR(ind(length(ind)/2+1:end)).*sin(tT(ind(length(ind)/2+1:end)))*1.01, ...
-tX(ind(length(ind)/2+1:end)), 80, 'filled', 'CData', C(length(ind)/2+1:end,:));
rgbLightHandles{end+1} = glowHdl;
rgbLightHandles{end+1} = coreHdl;
rgbLightColors{end+1} = C(length(ind)/2+1:end,:);
rgbLightColors{end+1} = C(length(ind)/2+1:end,:);
else
if i == 2 || i == 4
M = drawMembraneOrnament;
set(M, 'Matrix', makehgtform('Translate', [tR(ind(1)).*cos(tT(ind(1)))*.98, ...
tR(ind(1)).*sin(tT(ind(1)))*.98, -tX(ind(1))+0.05*0], 'ZRotate', rand*pi+pi/4*i, 'Scale', .9));
M = drawMembraneOrnament;
set(M, 'Matrix', makehgtform('Translate', [tR(ind(3)).*cos(tT(ind(3)))*.98, ...
tR(ind(3)).*sin(tT(ind(3)))*.98, -tX(ind(3))+0.05*0], 'ZRotate', rand*pi+pi/4*i, 'Scale', .9));
glowHdl = scatter3(tR(ind([2,4:end])).*cos(tT(ind([2,4:end])))*1.01, ...
tR(ind([2,4:end])).*sin(tT(ind([2,4:end])))*1.01, ...
-tX(ind([2,4:end])), 500, 'filled',...
'CData', C([2,4:end],:), 'MarkerEdgeColor','none', 'MarkerFaceAlpha',.3);
coreHdl = scatter3(tR(ind([2,4:end])).*cos(tT(ind([2,4:end])))*1.01, ...
tR(ind([2,4:end])).*sin(tT(ind([2,4:end])))*1.01, ...
-tX(ind([2,4:end])), 80, 'filled', 'CData', C([2,4:end],:));
rgbLightHandles{end+1} = glowHdl;
rgbLightHandles{end+1} = coreHdl;
rgbLightColors{end+1} = C([2,4:end],:);
rgbLightColors{end+1} = C([2,4:end],:);
else
glowHdl = scatter3(tR(ind).*cos(tT(ind))*1.01, tR(ind).*sin(tT(ind))*1.01, -tX(ind), 500, 'filled',...
'CData', C, 'MarkerEdgeColor','none', 'MarkerFaceAlpha',.3);
coreHdl = scatter3(tR(ind).*cos(tT(ind))*1.01, tR(ind).*sin(tT(ind))*1.01, -tX(ind), 80, 'filled', 'CData', C);
rgbLightHandles{end+1} = glowHdl;
rgbLightHandles{end+1} = coreHdl;
rgbLightColors{end+1} = C;
rgbLightColors{end+1} = C;
end
end
% Tree lights
if i == 1
arrayfun(@(h)light('Style','Local','Position',[tR(ind(h)).*cos(tT(ind(h)))*.7, ...
tR(ind(h)).*sin(tT(ind(h)))*.7, -tX(ind(h))+2],'Color',C(h,:)), length(ind)/2+1:length(ind));
end
end
%% Draw Tinsel/Garland (thicker, closer to tree, themed colors)
nTinsel = 800;
tinselT = linspace(0, 8*pi, nTinsel);
% Very close to tree - tight radius following tree cone
tinselR = linspace(1.0, 5.2, nTinsel);
tinselX = tinselR .* cos(tinselT);
tinselY = tinselR .* sin(tinselT);
tinselZ = -linspace(2.5, 14.2, nTinsel); % Shifted downwards
% Add slight waviness
tinselZ = tinselZ + 0.10 * sin(tinselT * 4);
% First strand - use turbo-like gradient (matches LED strips)
% Create color gradient along tinsel
tinselColors1 = turbo(nTinsel);
% Plot as individual segments for color gradient effect
for k = 1:10:nTinsel-10
plot3(tinselX(k:k+10), tinselY(k:k+10), tinselZ(k:k+10), ...
'Color', [tinselColors1(k,:), 0.85], 'LineWidth', 3);
end
% Second strand - offset, gold/warm color (matches star)
tinselT2 = tinselT + pi/2.5;
tinselX2 = tinselR .* cos(tinselT2);
tinselY2 = tinselR .* sin(tinselT2);
tinselZ2 = tinselZ + 0.1 * cos(tinselT * 3);
plot3(tinselX2, tinselY2, tinselZ2, 'Color', [255, 223, 153, 200]./255, 'LineWidth', 2.5);
% Third strand - warm gold/cream color (complementary to other strands)
tinselT3 = tinselT + pi;
tinselX3 = tinselR .* cos(tinselT3);
tinselY3 = tinselR .* sin(tinselT3);
tinselZ3 = tinselZ - 0.08 * sin(tinselT * 5);
plot3(tinselX3, tinselY3, tinselZ3, 'Color', [255, 200, 120, 180]./255, 'LineWidth', 2);
%% Draw Presents (spaced around tree, colorful, uniform height)
% Vibrant colors matching festive theme
presentColors = [255, 80, 80; % bright red
80, 220, 255; % bright cyan
255, 220, 80; % golden yellow
180, 100, 255]./255; % purple
% Red ribbons to match bow color
ribbonColors = [255, 80, 80; % red
255, 80, 80; % red
255, 80, 80; % red
255, 80, 80]./255; % red
% Bow color - festive red
bowColor = [255, 80, 80]./255;
% Spaced out in a ring around the tree
presentPositions = [-3.0, 2.5, groundZ;
3.2, 1.8, groundZ;
-2.5, -3.0, groundZ;
2.8, -2.5, groundZ];
% Uniform height (1.2), varied rectangular footprints
uniformHeight = 1.2;
presentSizes = [2.0, 1.4, uniformHeight;
1.5, 2.2, uniformHeight;
1.8, 1.3, uniformHeight;
1.4, 1.8, uniformHeight];
for p = 1:size(presentPositions, 1)
drawPresent(presentPositions(p,:), presentSizes(p,:), presentColors(p,:), ribbonColors(p,:), bowColor);
end
%% Draw Star of Bethlehem
Rx = @(V, theta) V * [1 0 0; 0 cos(theta) sin(theta); 0 -sin(theta) cos(theta)];
w = .3; RStar = .62; r = .4;
TStar = (1/8:1/8:(2 - 1/8)).' .* pi;
V8 = [0, 0, w; 0, 0, -w;
1, 0, 0; 0, 1, 0; -1, 0, 0; 0, -1, 0;
RStar, RStar, 0; -RStar, RStar, 0; -RStar, -RStar, 0; RStar, -RStar, 0;
cos(TStar).*r, sin(TStar).*r, TStar.*0];
F8 = [1,3,25; 1,3,11; 2,3,25; 2,3,11; 1,7,11; 1,7,13; 2,7,11; 2,7,13;
1,4,13; 1,4,15; 2,4,13; 2,4,15; 1,8,15; 1,8,17; 2,8,15; 2,8,17;
1,5,17; 1,5,19; 2,5,17; 2,5,19; 1,9,19; 1,9,21; 2,9,19; 2,9,21;
1,6,21; 1,6,23; 2,6,21; 2,6,23; 1,10,23; 1,10,25; 2,10,23; 2,10,25];
V8 = Rx(V8 .* .8, pi/2) + [0, 0, -1.3];
starPatch = patch('Faces', F8, 'Vertices', V8, 'FaceColor', [255,223,153]./255,...
'EdgeColor', [255,223,153]./255, 'FaceAlpha', 1, 'FaceLighting', 'gouraud', ...
'AmbientStrength', .7, 'DiffuseStrength', .8, 'SpecularStrength', 0);
%% Draw Snow (two populations) - deterministic for seamless loop
rng(42); % Fixed seed for reproducibility
nSnow1 = 90;
nSnow2 = 110;
nSnowTotal = nSnow1 + nSnow2;
% Initial positions (these will be restored at frame 360)
sXYZ_init = rand(nSnowTotal, 3) .* [12, 12, 19] - [6, 6, 18];
sXYZ = sXYZ_init;
% Fall speeds: snow must fall exactly N complete cycles in 360 frames
% Height range is 19 units (-18 to 1), so fall speed * 360 = N * 19
% For seamless loop: group 1 falls 2 cycles (38 units), group 2 falls 3 cycles (57 units)
fallSpeed1 = 2 * 19 / 360; % 2 complete cycles
fallSpeed2 = 3 * 19 / 360; % 3 complete cycles
sHdl1 = plot3(sXYZ(1:nSnow1, 1), sXYZ(1:nSnow1, 2), sXYZ(1:nSnow1, 3), '*', 'LineWidth', 1, ...
'MarkerSize', 12, 'Color', [1 1 1] * .9);
sHdl2 = plot3(sXYZ(nSnow1+1:end, 1), sXYZ(nSnow1+1:end, 2), sXYZ(nSnow1+1:end, 3), '.', ...
'MarkerSize', 10, 'Color', [1 1 1] * .9);
%% Draw Dust Motes / Ambient Particles - deterministic for seamless loop
nDust = 80;
dustXYZ_init = rand(nDust, 3) .* [10, 10, 16] - [5, 5, 16];
dustHdl = scatter3(dustXYZ_init(:,1), dustXYZ_init(:,2), dustXYZ_init(:,3), 8, 'filled', ...
'MarkerFaceColor', [1, 0.95, 0.8], 'MarkerFaceAlpha', 0.3, 'MarkerEdgeColor', 'none');
%% Animation Loop
frames = {};
step = 10;
for i = 1:step:360
% Snow fall - deterministic cyclic motion
% Calculate position based on frame number (not cumulative)
snowZ1 = sXYZ_init(1:nSnow1, 3) - fallSpeed1 * i;
snowZ2 = sXYZ_init(nSnow1+1:end, 3) - fallSpeed2 * i;
% Wrap using mod to keep in range [-18, 1] (height 19)
snowZ1 = mod(snowZ1 + 18, 19) - 18;
snowZ2 = mod(snowZ2 + 18, 19) - 18;
% Deterministic wind drift based on frame (sinusoidal, completes full cycles)
windX1 = 0.5 * sin(2 * pi * i / 360 + sXYZ_init(1:nSnow1, 3) * 0.2);
windX2 = 0.5 * sin(2 * pi * i / 360 + sXYZ_init(nSnow1+1:end, 3) * 0.2);
sHdl1.XData = sXYZ_init(1:nSnow1, 1) + windX1;
sHdl1.ZData = snowZ1;
sHdl2.XData = sXYZ_init(nSnow1+1:end, 1) + windX2;
sHdl2.ZData = snowZ2;
% Dust motes - sinusoidal motion (completes full cycles in 360 frames)
dustX = dustXYZ_init(:, 1) + 0.3 * sin(2 * pi * i / 360 + (1:nDust)' * 0.5);
dustY = dustXYZ_init(:, 2) + 0.3 * cos(2 * pi * i / 360 + (1:nDust)' * 0.3);
dustZ = dustXYZ_init(:, 3) + 0.2 * sin(2 * pi * i / 180 + (1:nDust)' * 0.2);
dustHdl.XData = dustX;
dustHdl.YData = dustY;
dustHdl.ZData = dustZ;
% RGB lights twinkle - vary brightness by modulating color intensity (subtle fade)
% Uses 2*pi*i/360 for seamless loop (completes integer cycles)
for k = 1:length(rgbLightHandles)
if isvalid(rgbLightHandles{k})
baseColors = rgbLightColors{k};
nLights = size(baseColors, 1);
% Each light twinkles at a different phase, fading between 70% and 100% brightness
% 12 complete cycles in 360 frames
twinkleFactor = 0.85 + 0.15 * sin(2 * pi * i * 12 / 360 + (1:nLights)' * pi / 3);
modulatedColors = baseColors .* twinkleFactor;
rgbLightHandles{k}.CData = modulatedColors;
end
end
% LED chase animation - use frame-based calculation for seamless loop
% Shift by amount that returns to start after 360 frames
for s = 1:length(surfaceHandles)
if isvalid(surfaceHandles(s))
[nRows, nCols] = size(surfaceHandles(s).CData);
baseData = linspace(1, cmapLen, nRows)';
shiftAmount = mod(i * 2, cmapLen); % 2 units per frame, wraps at cmapLen
newCol = mod(baseData + shiftAmount - 1, cmapLen) + 1;
surfaceHandles(s).CData = repmat(newCol, 1, nCols);
end
end
view([i, 30]);
drawnow;
frames{end+1} = getframe(gcf);
end
%% Helper Functions
function H = drawPinecone
% Ref: https://blogs.mathworks.com/graphics-and-apps/2024/12/16/pinecode-creating-pinecones-with-fibonacci-spirals/
nscales = 140;
height = 2.5;
nn = 18;
vps = 17;
vpr = 20;
FIB = (1+sqrt(5)) * (1:nscales);
H_pc = linspace(0, 1, nscales).^2.8 * height;
R_pc = 1 - linspace(-1, 1, nscales).^2;
U = 1 - abs(linspace(-1, 1, nscales).^3);
ST = reshape((linspace(-.5, .5, vps) * .3 + FIB')', 1, []);
SR = reshape(((1 - abs(linspace(-1, 1, vps).^3)) .* R_pc')', 1, []);
SH = reshape((ones(1, vps) .* H_pc' + abs(linspace(-1, 1, vps).^3 .* U') * .2)', 1, []);
MR = linspace(0, 1, vpr)' .* SR;
X = cospi(ST) .* MR;
Y = sinpi(ST) .* MR;
Z = SH .* ones(vpr, 1) + MR .* linspace(0, 1, vps*nscales).^2.1 * 1.2;
P(1) = plot3(X(end,:), Y(end,:), -Z(end,:), '-', 'LineWidth', .5, 'Color', [120 61 3]/255*.5);
P(2) = surface(X, Y, -Z, 'FaceColor', "#783D03", 'EdgeColor', 'none', 'FaceLighting', 'gouraud', ...
'AmbientStrength', 0.5, 'SpecularColorReflectance', 0.1, 'SpecularStrength', 0.1, ...
'SpecularExponent', 5, 'DiffuseStrength', 0.15);
P(3) = line('XData', (0:.5:2)/3*cos(rand*2*pi), 'YData', zeros(1,5), 'ZData', [0 .05 .2 .25 .2]*2, ...
'Color', 'k', 'LineWidth', 1);
NT = linspace(0, 5, nn)';
NR = linspace(.1, .8, nn)';
NV = [0 0 0; -ones(nn,1).*randi([-1,1], nn, 1)*.5, cospi(NT).*NR, sinpi(NT).*NR+.9];
NF = [ones(nn, 1), (1:nn)'+1];
P(4) = patch('Vertices', NV, 'Faces', NF, 'EdgeColor', '#0F9666', 'FaceColor', 'none', 'LineWidth', 1);
H = hgtransform;
set(P, 'Parent', H);
end
function H = drawMembraneOrnament
% Ref: https://uk.mathworks.com/help/matlab/visualize/creating-the-matlab-logo.html
L = membrane(1, 100);
L = L - max(L, [], 'all');
[x, y] = deal(linspace(-.5, .5, length(L)));
[r, c] = find(L == max(L, [], 'all'));
x = x + x(r);
y = y + y(c);
[X, Y] = meshgrid(x, y);
S(1) = surface(X, Y, L, 'EdgeColor', 'none');
view(3);
Z = S(1).ZData;
S(2) = surf([X(Y==Y(1))*[1 1]; NaN(1,2); X(X==X(1))*[1 1]; NaN(1,2); X(Y==Y(end))*[1 1]; NaN(1,2); X(X==X(end))*[1 1]; NaN(1,2); X([1,end],[1,end])],...
[Y(Y==Y(1))*[1 1]; NaN(1,2); Y(X==X(1))*[1 1]; NaN(1,2); Y(Y==Y(end))*[1 1]; NaN(1,2); Y(X==X(end))*[1 1]; NaN(1,2); Y([1,end],[1,end])],...
[[Z(Y==Y(1)), ones(size(Z(Y==Y(1))))*(min(Z,[],'all'))]; NaN(1,2); [Z(X==X(1)), ones(size(Z(X==X(1))))*(min(Z,[],'all'))]; NaN(1,2);...
[Z(Y==Y(end)), ones(size(Z(Y==Y(end))))*(min(Z,[],'all'))]; NaN(1,2); [Z(X==X(end)), ones(size(Z(X==X(end))))*(min(Z,[],'all'))];...
NaN(1,2); ones(2,2)*(min(Z,[],'all'))], 'EdgeColor', 'none');
set(S, 'FaceColor', '#FF4433', 'FaceLighting', 'gouraud', 'AmbientStrength', .3, ...
'DiffuseStrength', .6, 'BackFaceLighting', 'lit', 'SpecularStrength', 1, ...
'SpecularColorReflectance', 1, 'SpecularExponent', 7);
H = hgtransform;
set(S, 'Parent', H);
end
function drawPresent(pos, sz, faceColor, ribbonColor, bowColor)
% Draw a wrapped present box with thick ribbon - sits on ground, doesn't sink
x0 = pos(1) - sz(1)/2; % center the box at pos
y0 = pos(2) - sz(2)/2;
z0 = pos(3); % bottom sits exactly on ground
dx = sz(1); dy = sz(2); dz = sz(3);
% Box vertices
verts = [x0, y0, z0; % 1
x0+dx, y0, z0; % 2
x0+dx, y0+dy, z0; % 3
x0, y0+dy, z0; % 4
x0, y0, z0+dz; % 5
x0+dx, y0, z0+dz; % 6
x0+dx, y0+dy, z0+dz; % 7
x0, y0+dy, z0+dz]; % 8
faces = [1 2 3 4; % bottom
5 6 7 8; % top
1 2 6 5; % front
3 4 8 7; % back
1 4 8 5; % left
2 3 7 6]; % right
patch('Vertices', verts, 'Faces', faces, 'FaceColor', faceColor, ...
'EdgeColor', faceColor * 0.6, 'LineWidth', 1, 'FaceAlpha', 1, ...
'FaceLighting', 'gouraud', 'AmbientStrength', 0.5, 'DiffuseStrength', 0.6);
% Thick ribbon parameters
rw = 0.12 * max([dx, dy]);
ribbonOffset = 0.025;
% All ribbon patche
rx = x0 + dx/2;
ry = y0 + dy/2;
% Front face ribbon
patch('Vertices', [rx-rw, y0-ribbonOffset, z0; rx+rw, y0-ribbonOffset, z0; ...
rx+rw, y0-ribbonOffset, z0+dz; rx-rw, y0-ribbonOffset, z0+dz], ...
'Faces', [1 2 3 4], 'FaceColor', ribbonColor, 'EdgeColor', 'none', ...
'FaceLighting', 'none');
% Back face ribbon
patch('Vertices', [rx-rw, y0+dy+ribbonOffset, z0; rx+rw, y0+dy+ribbonOffset, z0; ...
rx+rw, y0+dy+ribbonOffset, z0+dz; rx-rw, y0+dy+ribbonOffset, z0+dz], ...
'Faces', [1 2 3 4], 'FaceColor', ribbonColor, 'EdgeColor', 'none', ...
'FaceLighting', 'none');
% Left face ribbon
patch('Vertices', [x0-ribbonOffset, ry-rw, z0; x0-ribbonOffset, ry+rw, z0; ...
x0-ribbonOffset, ry+rw, z0+dz; x0-ribbonOffset, ry-rw, z0+dz], ...
'Faces', [1 2 3 4], 'FaceColor', ribbonColor, 'EdgeColor', 'none', ...
'FaceLighting', 'none');
% Right face ribbon
patch('Vertices', [x0+dx+ribbonOffset, ry-rw, z0; x0+dx+ribbonOffset, ry+rw, z0; ...
x0+dx+ribbonOffset, ry+rw, z0+dz; x0+dx+ribbonOffset, ry-rw, z0+dz], ...
'Faces', [1 2 3 4], 'FaceColor', ribbonColor, 'EdgeColor', 'none', ...
'FaceLighting', 'none');
% Top ribbon strip (front-back direction)
patch('Vertices', [rx-rw, y0, z0+dz+ribbonOffset; rx+rw, y0, z0+dz+ribbonOffset; ...
rx+rw, y0+dy, z0+dz+ribbonOffset; rx-rw, y0+dy, z0+dz+ribbonOffset], ...
'Faces', [1 2 3 4], 'FaceColor', ribbonColor, 'EdgeColor', 'none', ...
'FaceLighting', 'none');
% Top ribbon strip (left-right direction)
patch('Vertices', [x0, ry-rw, z0+dz+ribbonOffset; x0+dx, ry-rw, z0+dz+ribbonOffset; ...
x0+dx, ry+rw, z0+dz+ribbonOffset; x0, ry+rw, z0+dz+ribbonOffset], ...
'Faces', [1 2 3 4], 'FaceColor', ribbonColor, 'EdgeColor', 'none', ...
'FaceLighting', 'none');
% Bow on top, festive color
bowX = x0 + dx/2;
bowY = y0 + dy/2;
bowZ = z0 + dz + ribbonOffset;
bowSize = rw * 3.2;
% Bow loops
scatter3(bowX - bowSize*0.4, bowY, bowZ + bowSize*0.25, 180, 'filled', ...
'MarkerFaceColor', bowColor, 'MarkerFaceAlpha', 1, 'MarkerEdgeColor', 'none');
scatter3(bowX + bowSize*0.4, bowY, bowZ + bowSize*0.25, 180, 'filled', ...
'MarkerFaceColor', bowColor, 'MarkerFaceAlpha', 1, 'MarkerEdgeColor', 'none');
% Center knot
scatter3(bowX, bowY, bowZ + bowSize*0.15, 100, 'filled', ...
'MarkerFaceColor', bowColor * 0.8, 'MarkerEdgeColor', 'none');
end
Mike Croucher
Mike Croucher on 16 Dec 2025
That looks fantastic!
Ned Gulley
Ned Gulley on 15 Dec 2025
My first result was chaos!
But it got better.
% Create figure with dark background for night time
figure('Color', [0.05 0.05 0.15], 'Position', [100 100 800 800]);
ax = axes('Position', [0.05 0.05 0.9 0.9]);
set(ax, 'Color', [0.05 0.05 0.15]);
hold on;
% Get the L-shaped membrane data
Z = membrane(1);
[m, n] = size(Z);
[X, Y] = meshgrid(1:n, 1:m);
% Center the coordinates around origin
X = X - mean(X(:));
Y = Y - mean(Y(:));
Z = Z - mean(Z(:));
% Stretch Z extent by factor of 5
Z = Z * 5;
% Create the base membrane surface
baseSurf = surf(X, Y, Z);
set(baseSurf, 'FaceColor', [0.1 0.6 0.2], 'EdgeColor', 'none', ...
'FaceLighting', 'gouraud', 'AmbientStrength', 0.5);
% Create transform for base layer
baseTransform = hgtransform('Parent', ax);
set(baseSurf, 'Parent', baseTransform);
% Number of tree layers
numLayers = 15;
% Store handles for all tree layers
treeHandles = baseTransform;
% Set initial transform for base layer (layer 1)
i = 1;
scaleXY = 1.0 - 0.06 * (i - 1); % X-Y scale shrinks with height (adjusted for 15 layers)
scaleZ = 1.0; % Keep Z scale constant
rotAngle1 = 40 * i;
Rz1 = makehgtform('zrotate', deg2rad(rotAngle1));
S1 = makehgtform('scale', [scaleXY scaleXY scaleZ]);
set(baseTransform, 'Matrix', Rz1 * S1);
% Create the tree by cloning and transforming membrane copies
for i = 2:numLayers
% Clone the base surface
clonedSurf = copyobj(baseSurf, ax);
% Create transform object for this layer
t = hgtransform('Parent', ax);
set(clonedSurf, 'Parent', t);
% Calculate X-Y scale factor (shrinks as we go up for conical shape)
scaleXY = 1.0 - 0.06 * (i - 1);
% Keep Z scale constant (don't shrink vertical extent)
scaleZ = 1.0;
% Calculate rotation angle (spins each layer)
rotAngle = 40 * i;
% Calculate vertical position (stacks upward with overlap)
zOffset = 3.0 * (i - 1);
% Create transformation matrix
% Rotation around Z axis
Rz = makehgtform('zrotate', deg2rad(rotAngle));
% Scale (different for X-Y vs Z)
S = makehgtform('scale', [scaleXY scaleXY scaleZ]);
% Translation
T = makehgtform('translate', [0, 0, zOffset]);
% Apply combined transformation
set(t, 'Matrix', T * Rz * S);
treeHandles = [treeHandles; t];
end
% Add snowy ground
[Xground, Yground] = meshgrid(-20:2:20, -20:2:20);
Zground = -5 + 0.3*randn(size(Xground)); % Slight variation for snow texture
groundSurf = surf(Xground, Yground, Zground);
set(groundSurf, 'FaceColor', [0.95 0.95 1.0], 'EdgeColor', 'none', ...
'FaceLighting', 'gouraud', 'AmbientStrength', 0.7, 'DiffuseStrength', 0.6);
% Add sphere ornaments scattered on the tree
numOrnaments = 30;
ornamentColors = [
1.0, 0.0, 0.0; % Red
1.0, 0.8, 0.0; % Gold
0.0, 0.5, 1.0; % Blue
1.0, 0.0, 1.0; % Magenta
0.0, 1.0, 1.0; % Cyan
1.0, 0.5, 0.0; % Orange
0.8, 0.0, 0.8; % Purple
1.0, 1.0, 0.0; % Yellow
1.0, 0.2, 0.6; % Pink
0.5, 1.0, 0.0; % Lime
];
% Find edge points of the original membrane
% The edges are the boundary of the L-shaped membrane
[m, n] = size(X);
edgeRows = [];
edgeCols = [];
% Top edge
edgeRows = [edgeRows, ones(1, n)];
edgeCols = [edgeCols, 1:n];
% Bottom edge
edgeRows = [edgeRows, m*ones(1, n)];
edgeCols = [edgeCols, 1:n];
% Left edge (excluding corners already added)
edgeRows = [edgeRows, 2:m-1];
edgeCols = [edgeCols, ones(1, m-2)];
% Right edge (excluding corners already added)
edgeRows = [edgeRows, 2:m-1];
edgeCols = [edgeCols, n*ones(1, m-2)];
% Sample random edge points for ornaments
numEdgePoints = length(edgeRows);
ornamentCount = 0;
for i = 1:numOrnaments
% Random layer
layer = randi([1, numLayers]);
% Random edge point
edgeIdx = randi(numEdgePoints);
row = edgeRows(edgeIdx);
col = edgeCols(edgeIdx);
% Get the base coordinates at this edge point
xBase = X(row, col);
yBase = Y(row, col);
zBase = Z(row, col);
% Apply the same transformation as the layer
scaleXY = 1.0 - 0.06 * (layer - 1);
rotAngle = 40 * layer;
zOffset = 3.0 * (layer - 1);
% Transform the point
cosTheta = cosd(rotAngle);
sinTheta = sind(rotAngle);
xPos = scaleXY * (xBase * cosTheta - yBase * sinTheta);
yPos = scaleXY * (xBase * sinTheta + yBase * cosTheta);
zPos = zBase + zOffset;
% Create sphere
[Xs, Ys, Zs] = sphere(20);
sphereSize = 0.5 + rand() * 0.3;
Xs = Xs * sphereSize + xPos;
Ys = Ys * sphereSize + yPos;
Zs = Zs * sphereSize + zPos;
% Random color
colorIdx = randi(size(ornamentColors, 1));
% Draw sphere
ornament = surf(Xs, Ys, Zs);
set(ornament, 'FaceColor', ornamentColors(colorIdx, :), ...
'EdgeColor', 'none', 'FaceLighting', 'gouraud', ...
'AmbientStrength', 0.5, 'DiffuseStrength', 0.8, 'SpecularStrength', 0.9);
end
% Set up the view
axis vis3d;
axis off;
view(3);
camlight('headlight');
lighting gouraud;
material([0.4 0.6 0.1])
% Set axis limits to frame the tree and ground nicely
axis([-27 27 -22 31 -30 68]);
title('Christmas Tree from L-Shaped Membrane', 'FontSize', 16, 'FontWeight', 'bold', 'Color', 'w');
% Rotate for better initial view
view(45, 20);
Seth DeLand
Seth DeLand on 15 Dec 2025
Prompt: "Write the MATLAB code for drawing a Christmas tree. The tree should have a star, ornaments, and a presents beneath it."
Code:
% Christmas Tree with Star, Ornaments, and Presents
% Clear workspace and create figure
clear; clc; close all;
figure('Color', [0.1 0.1 0.3], 'Position', [100 100 600 700]);
hold on;
axis equal;
axis off;
xlim([-10 10]);
ylim([-3 18]);
% === TREE TRUNK ===
trunkX = [-1 1 1 -1];
trunkY = [0 0 2 2];
fill(trunkX, trunkY, [0.4 0.2 0.1], 'EdgeColor', 'none');
% === TREE LAYERS (3 triangular sections) ===
treeGreen = [0.1 0.5 0.1];
% Bottom layer
layer1X = [-6 6 0];
layer1Y = [2 2 7];
fill(layer1X, layer1Y, treeGreen, 'EdgeColor', [0 0.3 0], 'LineWidth', 2);
% Middle layer
layer2X = [-4.5 4.5 0];
layer2Y = [5 5 11];
fill(layer2X, layer2Y, treeGreen, 'EdgeColor', [0 0.3 0], 'LineWidth', 2);
% Top layer
layer3X = [-3 3 0];
layer3Y = [9 9 14];
fill(layer3X, layer3Y, treeGreen, 'EdgeColor', [0 0.3 0], 'LineWidth', 2);
% === STAR ON TOP ===
starPoints = 5;
outerR = 1.5;
innerR = 0.6;
starCenterY = 14.8;
theta = linspace(pi/2, pi/2 + 2*pi, 2*starPoints + 1);
radii = repmat([outerR innerR], 1, starPoints);
radii = [radii outerR];
starX = radii .* cos(theta);
starY = radii .* sin(theta) + starCenterY;
fill(starX, starY, [1 0.85 0], 'EdgeColor', [0.9 0.7 0], 'LineWidth', 2);
% === ORNAMENTS ===
ornamentColors = [1 0 0; 0 0 1; 1 0.8 0; 1 0 1; 0 1 1; 1 0.5 0];
ornamentX = [-3.5 -1 2 3.5 -2.5 1.5 0 -2 2.5 -1 1 0];
ornamentY = [3 3.5 3.5 3 5.5 5.5 4 6.5 7 10 10.5 12];
for i = 1:length(ornamentX)
colorIdx = mod(i-1, size(ornamentColors,1)) + 1;
theta = linspace(0, 2*pi, 50);
ornRadius = 0.4;
ox = ornRadius * cos(theta) + ornamentX(i);
oy = ornRadius * sin(theta) + ornamentY(i);
fill(ox, oy, ornamentColors(colorIdx,:), 'EdgeColor', 'w', 'LineWidth', 1);
% Add shine to ornaments
plot(ornamentX(i)-0.15, ornamentY(i)+0.15, '.', 'Color', 'w', 'MarkerSize', 8);
end
% === TINSEL/GARLAND (wavy lines) ===
tinselY1 = linspace(3, 6, 50);
tinselX1 = 0.3*sin(8*tinselY1) .* linspace(5, 2, 50);
plot(tinselX1, tinselY1, 'Color', [1 0.85 0.4], 'LineWidth', 2);
tinselY2 = linspace(6, 10, 40);
tinselX2 = 0.25*sin(8*tinselY2) .* linspace(3.5, 1.5, 40);
plot(tinselX2, tinselY2, 'Color', [1 0.85 0.4], 'LineWidth', 2);
% === PRESENTS ===
% Present 1 (red with gold ribbon)
fill([-5.5 -3 -3 -5.5], [-2.5 -2.5 -0.5 -0.5], [0.8 0.1 0.1], 'EdgeColor', 'k', 'LineWidth', 1);
fill([-4.5 -4 -4 -4.5], [-2.5 -2.5 -0.5 -0.5], [1 0.8 0], 'EdgeColor', 'none'); % vertical ribbon
fill([-5.5 -3 -3 -5.5], [-1.7 -1.7 -1.3 -1.3], [1 0.8 0], 'EdgeColor', 'none'); % horizontal ribbon
% Bow
fill([-4.8 -4.25 -4.25], [-0.5 -0.5 0.1], [1 0.8 0], 'EdgeColor', [0.8 0.6 0]);
fill([-4.25 -3.7 -4.25], [-0.5 -0.5 0.1], [1 0.8 0], 'EdgeColor', [0.8 0.6 0]);
% Present 2 (blue with white ribbon)
fill([3 5.5 5.5 3], [-2.5 -2.5 -1 -1], [0.1 0.3 0.8], 'EdgeColor', 'k', 'LineWidth', 1);
fill([4 4.5 4.5 4], [-2.5 -2.5 -1 -1], [1 1 1], 'EdgeColor', 'none');
fill([3 5.5 5.5 3], [-1.9 -1.9 -1.6 -1.6], [1 1 1], 'EdgeColor', 'none');
% Bow
fill([3.7 4.25 4.25], [-1 -1 -0.5], [1 1 1], 'EdgeColor', [0.8 0.8 0.8]);
fill([4.25 4.8 4.25], [-1 -1 -0.5], [1 1 1], 'EdgeColor', [0.8 0.8 0.8]);
% Present 3 (green with red ribbon)
fill([-1.5 1.5 1.5 -1.5], [-2.5 -2.5 0 0], [0.2 0.6 0.2], 'EdgeColor', 'k', 'LineWidth', 1);
fill([-0.25 0.25 0.25 -0.25], [-2.5 -2.5 0 0], [0.9 0.1 0.1], 'EdgeColor', 'none');
fill([-1.5 1.5 1.5 -1.5], [-1.5 -1.5 -1 -1], [0.9 0.1 0.1], 'EdgeColor', 'none');
% Bow
fill([-0.6 0 0], [0 0 0.6], [0.9 0.1 0.1], 'EdgeColor', [0.7 0 0]);
fill([0 0.6 0], [0 0 0.6], [0.9 0.1 0.1], 'EdgeColor', [0.7 0 0]);
% === SNOW ON GROUND ===
for i = 1:100
sx = rand*20 - 10;
sy = rand*0.8 - 2.8;
plot(sx, sy, '.', 'Color', 'w', 'MarkerSize', randi([3,8]));
end
% === FALLING SNOW ===
for i = 1:60
sx = rand*20 - 10;
sy = rand*18;
plot(sx, sy, '.', 'Color', [1 1 1 0.7], 'MarkerSize', randi([2,6]));
end
% === TITLE ===
text(0, 17, 'Merry Christmas!', 'Color', [1 0.9 0.9], ...
'FontSize', 18, 'FontWeight', 'bold', 'HorizontalAlignment', 'center');
hold off;
Mike Croucher
Mike Croucher on 16 Dec 2025
Which AI did you use?
Seth DeLand
Seth DeLand on 16 Dec 2025
I used Claude for this.
Seth DeLand
Seth DeLand on 16 Dec 2025
Here is my version from MATLAB Copilot, with kind regards to my friends in Denmark and Sweden.
Hans Scharler
Hans Scharler on 16 Dec 2025
MATLAB Copilot
Toshiaki Takeuchi
Toshiaki Takeuchi on 15 Dec 2025
Here is my attempt with Claude.
christmas_tree()
ans =
Figure (1) with properties: Number: 1 Name: '' Color: [0 0 0] Position: [100 100 800 800] Units: 'pixels' Use GET to show all properties
function fig = christmas_tree(varargin)
% CHRISTMAS_TREE Creates an interactive 3D Christmas tree visualization
%
% Syntax:
% fig = christmas_tree()
% fig = christmas_tree('Name', Value, ...)
%
% Parameters (Name-Value pairs):
% 'Height' - Tree height (default: 5)
% 'BaseRadius' - Base radius (default: 1.5)
% 'NumTiers' - Tree layers (default: 4)
% 'NumOrnaments' - Ornament count (default: 20)
% 'NumLights' - String lights (default: 50)
% 'TreeColor' - Base green [R G B] (default: [0.1, 0.5, 0.2])
% 'ShowStar' - Display star (default: true)
% 'ShowGarland' - Display garland (default: true)
%
% Example:
% fig = christmas_tree();
% fig = christmas_tree('Height', 6, 'NumOrnaments', 30);
% Parse input arguments
p = inputParser;
addParameter(p, 'Height', 5, @(x) isnumeric(x) && x > 0);
addParameter(p, 'BaseRadius', 1.5, @(x) isnumeric(x) && x > 0);
addParameter(p, 'NumTiers', 4, @(x) isnumeric(x) && x > 0);
addParameter(p, 'NumOrnaments', 20, @(x) isnumeric(x) && x >= 0);
addParameter(p, 'NumLights', 50, @(x) isnumeric(x) && x >= 0);
addParameter(p, 'TreeColor', [0.1, 0.5, 0.2], @(x) isnumeric(x) && length(x) == 3);
addParameter(p, 'ShowStar', true, @islogical);
addParameter(p, 'ShowGarland', true, @islogical);
parse(p, varargin{:});
% Extract parameters
tree_height = p.Results.Height;
base_radius = p.Results.BaseRadius;
num_tiers = p.Results.NumTiers;
num_ornaments = p.Results.NumOrnaments;
num_lights = p.Results.NumLights;
tree_color = p.Results.TreeColor;
show_star = p.Results.ShowStar;
show_garland = p.Results.ShowGarland;
% Setup figure and axes with dark background
fig = figure('Color', 'k', 'Position', [100, 100, 800, 800]);
ax = axes('Color', 'k', 'XColor', 'none', 'YColor', 'none', 'ZColor', 'none');
hold on;
% Tier parameters: [radius_multiplier, height_position]
tier_params = [
0.95, 0.15; % Bottom tier (widest)
0.70, 0.40; % Mid-lower
0.45, 0.60; % Mid-upper
0.20, 0.80; % Top tier (narrowest)
];
% Create trunk
trunk_height = tree_height * 0.15;
trunk_radius = base_radius * 0.10;
create_trunk(trunk_height, trunk_radius);
% Create tree tiers
for i = 1:size(tier_params, 1)
radius_mult = tier_params(i, 1);
height_pos = tier_params(i, 2);
tier_radius = base_radius * radius_mult;
tier_z = trunk_height + tree_height * height_pos;
tier_height = tree_height * 0.25; % Each tier height
create_tree_tier(tier_radius, tier_height, tier_z, tree_color);
end
% Add ornaments
if num_ornaments > 0
positions = generate_ornament_positions(num_ornaments, tree_height, ...
base_radius, trunk_height, tier_params);
ornament_colors = [
1.0, 0.0, 0.0; % Red
1.0, 0.84, 0.0; % Gold
0.0, 0.5, 1.0; % Blue
0.9, 0.9, 0.9; % Silver
0.7, 0.0, 0.7; % Purple
];
for i = 1:size(positions, 1)
color_idx = mod(i-1, size(ornament_colors, 1)) + 1;
place_ornament(positions(i,:), 0.08, ornament_colors(color_idx,:));
end
end
% Add string lights with glow
if num_lights > 0
[light_x, light_y, light_z, light_colors] = create_light_spiral(...
num_lights, tree_height, base_radius, trunk_height, 3.5);
% Main lights
scatter3(light_x, light_y, light_z, 100, light_colors, ...
'filled', 'MarkerFaceAlpha', 0.9);
% Glow halos (3 layers)
for j = 1:3
glow_scale = 1 + j*0.5;
alpha_glow = 0.3 / j;
scatter3(light_x, light_y, light_z, ...
100*glow_scale^2, light_colors, ...
'filled', 'MarkerFaceAlpha', alpha_glow);
end
end
% Add garland
if show_garland
[garland_x, garland_y, garland_z] = create_garland_spiral(...
200, tree_height, base_radius, trunk_height, 5);
plot3(garland_x, garland_y, garland_z, ...
'Color', [1, 0.84, 0.2], 'LineWidth', 3);
end
% Add star topper
if show_star
star_z = trunk_height + tree_height;
create_star(star_z, 0.3, [1, 0.84, 0]);
end
% Lighting setup (3 lights for dimensional appearance)
light('Position', [-4, -3, 8], ...
'Style', 'infinite', ...
'Color', [1.0, 0.95, 0.9]); % Warm white
light('Position', [5, 4, 3], ...
'Style', 'infinite', ...
'Color', [0.4, 0.4, 0.6]); % Cool fill
light('Position', [0, 0, -2], ...
'Style', 'infinite', ...
'Color', [0.2, 0.2, 0.3]); % Bottom light
% Material properties
material dull;
% Camera and view settings
axis equal off;
view([30, 20]);
camva(35);
% Interactive controls
title('Christmas Tree - Left click: Rotate | Right click: Pan | Scroll: Zoom', ...
'Color', 'w', 'FontSize', 12);
cameratoolbar('Show');
cameratoolbar('SetMode', 'orbit');
set(gcf, 'WindowButtonDownFcn', @mouseDown);
end
%% Helper Functions
function create_trunk(height, radius)
% Creates brown cylindrical trunk
[x, y, z] = cylinder(radius, 30);
z = z * height; % Scale to trunk height
trunk_color = [0.4, 0.25, 0.15]; % Brown
surf(x, y, z, ...
'EdgeColor', 'none', ...
'FaceColor', trunk_color, ...
'FaceLighting', 'gouraud', ...
'AmbientStrength', 0.4, ...
'DiffuseStrength', 0.7, ...
'SpecularStrength', 0.1);
end
function create_tree_tier(tier_radius, tier_height, z_offset, tree_color)
% Creates one tier of the tree with texture and color variation
n_points = 100; % High resolution for smooth appearance
% Create cone profile (wide at bottom, narrow at top)
theta = linspace(0, 2*pi, n_points);
height_profile = linspace(0, 1, 30); % Vertical resolution
% Build cone geometry with noise for branch-like texture
[THETA, H] = meshgrid(theta, height_profile);
% Radius decreases linearly from base to tip
R = tier_radius * (1 - H);
% Add random noise for branch texture
noise = 1 + 0.1 * randn(size(R));
R = R .* noise;
% Convert to Cartesian coordinates
X = R .* cos(THETA);
Y = R .* sin(THETA);
Z = z_offset - H * tier_height; % Tier hangs down from z_offset
% Create color gradient (darker at center, lighter at tips)
green_base = tree_color;
green_light = tree_color + [0.1, 0.2, 0.1]; % Lighter green
% Color based on height (darker at top/center, lighter at edges)
color_data = rescale(H, 0, 1);
% Render the tier
surf(X, Y, Z, color_data, ...
'EdgeColor', 'none', ...
'FaceColor', 'interp', ...
'FaceLighting', 'gouraud', ...
'AmbientStrength', 0.4, ...
'DiffuseStrength', 0.7, ...
'SpecularStrength', 0.1);
% Apply green colormap for this surface
% Note: This will affect all surfaces, but we set it last
green_map = [linspace(green_base(1), green_light(1), 256)', ...
linspace(green_base(2), green_light(2), 256)', ...
linspace(green_base(3), green_light(3), 256)'];
colormap(green_map);
end
function positions = generate_ornament_positions(n_ornaments, tree_height, ...
base_radius, trunk_height, tier_params)
% Generates random positions for ornaments on the tree surface
% Avoids top 20% (star) and bottom 10% (trunk)
positions = zeros(n_ornaments, 3);
% Valid height range (middle 70%)
min_height_ratio = 0.15;
max_height_ratio = 0.75;
for i = 1:n_ornaments
% Random height within valid range
h_ratio = min_height_ratio + (max_height_ratio - min_height_ratio) * rand();
h = trunk_height + tree_height * h_ratio;
% Calculate radius at this height based on cone taper
% Find which tier this height corresponds to
r_at_h = base_radius * (1 - h_ratio) * 0.85; % Slightly inside tree surface
% Random angle around tree
theta = rand() * 2 * pi;
% Position on tree surface
positions(i,:) = [
r_at_h * cos(theta), ...
r_at_h * sin(theta), ...
h
];
end
end
function place_ornament(position, radius, color)
% Renders a single ornament sphere at the given position
[xs, ys, zs] = sphere(30);
surf(xs*radius + position(1), ...
ys*radius + position(2), ...
zs*radius + position(3), ...
'FaceColor', color, ...
'EdgeColor', 'none', ...
'FaceLighting', 'gouraud', ...
'AmbientStrength', 0.3, ...
'DiffuseStrength', 0.6, ...
'SpecularStrength', 0.9); % High specular for shiny ornaments
end
function [x, y, z, colors] = create_light_spiral(n_lights, tree_height, ...
base_radius, trunk_height, n_wraps)
% Generates spiral path and colors for string lights
t = linspace(0, 1, n_lights);
% Calculate spiral coordinates (bottom to near-top)
height_pos = trunk_height + tree_height * (0.1 + 0.8*t);
angle = t * n_wraps * 2*pi;
% Radius follows cone taper
height_ratio = (height_pos - trunk_height) / tree_height;
radius_pos = base_radius * (1 - height_ratio) * 0.95;
% Light positions
x = radius_pos .* cos(angle);
y = radius_pos .* sin(angle);
z = height_pos;
% Light colors (warm white with variation)
colors = rand(n_lights, 3);
colors(:,1) = 0.8 + 0.2*rand(n_lights, 1); % More red
colors(:,2) = 0.6 + 0.4*rand(n_lights, 1); % Medium green
colors(:,3) = 0.3 + 0.7*rand(n_lights, 1); % Variable blue
end
function [x, y, z] = create_garland_spiral(n_points, tree_height, base_radius, ...
trunk_height, n_wraps)
% Generates spiral path for garland
t = linspace(0, 1, n_points);
% Calculate spiral coordinates
height_pos = trunk_height + tree_height * (0.15 + 0.8*t);
angle = t * n_wraps * 2*pi;
% Radius follows cone taper (slightly outside tree)
height_ratio = (height_pos - trunk_height) / tree_height;
radius_pos = base_radius * (1 - height_ratio) * 1.0;
% Garland positions
x = radius_pos .* cos(angle);
y = radius_pos .* sin(angle);
z = height_pos;
end
function create_star(star_z, star_size, star_color)
% Creates a 3D star with glow at tree top
% Create 5-pointed star profile
n_points = 5;
angles = linspace(0, 2*pi, n_points*2+1);
radii = repmat([star_size, star_size*0.5], 1, n_points);
radii = [radii, radii(1)]; % Close the shape
star_x = radii .* cos(angles);
star_y = radii .* sin(angles);
% Create front and back faces
z_front = star_z + 0.2;
z_back = star_z + 0.05;
% Front face
fill3(star_x, star_y, ones(size(star_x))*z_front, star_color, ...
'EdgeColor', 'none', ...
'FaceAlpha', 1.0, ...
'FaceLighting', 'gouraud', ...
'AmbientStrength', 0.8);
% Back face
fill3(star_x, star_y, ones(size(star_x))*z_back, star_color, ...
'EdgeColor', 'none', ...
'FaceAlpha', 1.0, ...
'FaceLighting', 'gouraud', ...
'AmbientStrength', 0.8);
% Add glow effect (multiple transparent layers)
[xs, ys, zs] = sphere(30);
for i = 1:8
glow_scale = star_size * (1 + i*0.15);
alpha_val = 0.3 / sqrt(i);
surf(xs*glow_scale, ys*glow_scale, zs*glow_scale + star_z + 0.125, ...
'EdgeColor', 'none', ...
'FaceColor', star_color, ...
'FaceAlpha', alpha_val, ...
'FaceLighting', 'none');
end
end
function mouseDown(src, ~)
% Mouse interaction callback
selType = get(src, 'SelectionType');
switch selType
case 'normal' % Left click - rotate
cameratoolbar('SetMode', 'orbit');
rotate3d on;
case 'alt' % Right click - pan
cameratoolbar('SetMode', 'pan');
pan on;
end
end
Hans Scharler
Hans Scharler on 15 Dec 2025 (Edited on 15 Dec 2025)
Yann Debray
Yann Debray on 15 Dec 2025
How about this (opus 4.5):
function christmasTree()
%CHRISTMASTREE Animated 3D Christmas tree with lights, ornaments, and snow
% Run christmasTree() to display a festive animated Christmas tree
% Create figure
fig = figure('Color', [0.05 0.05 0.15], 'Name', 'Merry Christmas!', ...
'NumberTitle', 'off', 'Position', [100 100 800 700]);
ax = axes('Parent', fig);
hold(ax, 'on');
% Set up 3D view
view(ax, 30, 20);
axis(ax, 'equal');
axis(ax, [-3 3 -3 3 0 6]);
set(ax, 'Color', [0.05 0.05 0.15], 'XColor', 'none', 'YColor', 'none', 'ZColor', 'none');
% Draw tree (stacked cones)
treeColor = [0.1 0.5 0.1];
drawTreeLayer(ax, 0, 0, 0.5, 2.0, 1.8, treeColor);
drawTreeLayer(ax, 0, 0, 1.8, 1.6, 1.5, treeColor * 1.1);
drawTreeLayer(ax, 0, 0, 2.8, 1.2, 1.3, treeColor * 1.2);
drawTreeLayer(ax, 0, 0, 3.6, 0.8, 1.0, treeColor * 1.3);
% Draw trunk
[xc, yc, zc] = cylinder(0.3, 20);
zc = zc * 0.5;
surf(ax, xc, yc, zc, 'FaceColor', [0.4 0.2 0.1], 'EdgeColor', 'none');
% Create ornaments
numOrnaments = 30;
ornamentColors = [1 0 0; 1 0.8 0; 0 0.5 1; 1 0 1; 0.8 0.8 0.8];
ornaments = struct('handle', {}, 'baseColor', {}, 'pos', {});
for i = 1:numOrnaments
theta = rand * 2 * pi;
h = 0.8 + rand * 3.2;
maxR = getTreeRadius(h);
r = (0.6 + 0.35 * rand) * maxR;
x = r * cos(theta);
y = r * sin(theta);
z = h;
colorIdx = randi(size(ornamentColors, 1));
baseColor = ornamentColors(colorIdx, :);
[xs, ys, zs] = sphere(10);
xs = xs * 0.12 + x;
ys = ys * 0.12 + y;
zs = zs * 0.12 + z;
hOrn = surf(ax, xs, ys, zs, 'FaceColor', baseColor, 'EdgeColor', 'none', ...
'FaceLighting', 'gouraud', 'AmbientStrength', 0.5);
ornaments(i).handle = hOrn;
ornaments(i).baseColor = baseColor;
ornaments(i).pos = [x, y, z];
end
% Create string lights wrapping around tree
numLights = 60;
lights = struct('handle', {}, 'color', {}, 'phase', {});
lightColors = [1 1 0.3; 1 0.3 0.3; 0.3 1 0.3; 0.3 0.5 1; 1 0.5 1];
for i = 1:numLights
t = (i - 1) / numLights;
h = 0.6 + t * 3.8;
theta = t * 8 * pi;
r = getTreeRadius(h) * 0.95;
x = r * cos(theta);
y = r * sin(theta);
z = h;
colorIdx = mod(i-1, size(lightColors, 1)) + 1;
hLight = plot3(ax, x, y, z, '.', 'MarkerSize', 15, ...
'Color', lightColors(colorIdx, :));
lights(i).handle = hLight;
lights(i).color = lightColors(colorIdx, :);
lights(i).phase = rand * 2 * pi;
end
% Create star on top
starZ = 4.5;
starPoints = 5;
starOuter = 0.4;
starInner = 0.15;
starTheta = linspace(0, 2*pi, 2*starPoints + 1);
starR = repmat([starOuter starInner], 1, starPoints);
starR = [starR starOuter];
starX = starR .* cos(starTheta + pi/2);
starY = starR .* sin(starTheta + pi/2);
starZcoord = ones(size(starX)) * starZ;
hStar = fill3(ax, starX, starY, starZcoord, [1 0.85 0], ...
'EdgeColor', [1 0.9 0.3], 'LineWidth', 2);
% Star glow
[xs, ys, zs] = sphere(15);
hStarGlow = surf(ax, xs*0.3, ys*0.3, zs*0.3 + starZ, ...
'FaceColor', [1 1 0.5], 'EdgeColor', 'none', 'FaceAlpha', 0.3);
% Create snow particles
numSnow = 150;
snowX = (rand(numSnow, 1) - 0.5) * 8;
snowY = (rand(numSnow, 1) - 0.5) * 8;
snowZ = rand(numSnow, 1) * 6;
snowSpeed = 0.02 + rand(numSnow, 1) * 0.03;
snowDrift = (rand(numSnow, 1) - 0.5) * 0.02;
hSnow = plot3(ax, snowX, snowY, snowZ, '.', 'Color', [1 1 1], ...
'MarkerSize', 4);
% Add lighting
light(ax, 'Position', [5 5 10], 'Style', 'infinite');
light(ax, 'Position', [-5 -5 5], 'Style', 'infinite', 'Color', [0.3 0.3 0.5]);
% Add title
title(ax, '🎄 Merry Christmas! 🎄', 'Color', [0.9 0.9 0.9], ...
'FontSize', 20, 'FontWeight', 'bold');
% Animation loop
frame = 0;
while ishandle(fig)
frame = frame + 1;
t = frame * 0.05;
% Twinkle lights
for i = 1:length(lights)
brightness = 0.5 + 0.5 * sin(t * 3 + lights(i).phase);
newColor = lights(i).color * brightness;
markerSize = 10 + 10 * brightness;
set(lights(i).handle, 'Color', newColor, 'MarkerSize', markerSize);
end
% Subtle ornament shimmer
for i = 1:length(ornaments)
shimmer = 0.85 + 0.15 * sin(t * 2 + i);
newColor = min(1, ornaments(i).baseColor * shimmer);
set(ornaments(i).handle, 'FaceColor', newColor);
end
% Rotate star
starTheta = linspace(0, 2*pi, 2*starPoints + 1);
starR = repmat([starOuter starInner], 1, starPoints);
starR = [starR starOuter];
rotation = t * 0.5;
starX = starR .* cos(starTheta + pi/2 + rotation);
starY = starR .* sin(starTheta + pi/2 + rotation);
set(hStar, 'XData', starX, 'YData', starY);
% Star glow pulse
glowScale = 0.3 + 0.1 * sin(t * 2);
set(hStarGlow, 'XData', xs*glowScale, 'YData', ys*glowScale);
% Animate snow
snowZ = snowZ - snowSpeed;
snowX = snowX + snowDrift .* sin(t + snowX);
% Reset snow that falls below ground
fallen = snowZ < 0;
snowZ(fallen) = 6;
snowX(fallen) = (rand(sum(fallen), 1) - 0.5) * 8;
snowY(fallen) = (rand(sum(fallen), 1) - 0.5) * 8;
set(hSnow, 'XData', snowX, 'YData', snowY, 'ZData', snowZ);
% Slow camera rotation
view(ax, 30 + t * 5, 20);
drawnow;
pause(0.03);
end
end
function drawTreeLayer(ax, cx, cy, baseZ, radius, height, color)
% Draw a cone-shaped tree layer
[theta, h] = meshgrid(linspace(0, 2*pi, 40), linspace(0, 1, 20));
r = radius * (1 - h);
% Add some irregularity for natural look
r = r .* (1 + 0.1 * sin(8 * theta));
x = cx + r .* cos(theta);
y = cy + r .* sin(theta);
z = baseZ + h * height;
surf(ax, x, y, z, 'FaceColor', color, 'EdgeColor', 'none', ...
'FaceLighting', 'gouraud', 'AmbientStrength', 0.4);
end
function r = getTreeRadius(h)
% Get approximate tree radius at height h
if h < 0.5
r = 0;
elseif h < 2.3
r = 2.0 * (1 - (h - 0.5) / 1.8);
elseif h < 3.3
r = 1.6 * (1 - (h - 1.8) / 1.5);
elseif h < 4.1
r = 1.2 * (1 - (h - 2.8) / 1.3);
else
r = 0.8 * (1 - (h - 3.6) / 1.0);
end
r = max(0.2, r);
end
David
David on 15 Dec 2025
I like how the star is positioned horizontally at the top :)
Seth DeLand
Seth DeLand on 15 Dec 2025
Reminds me of a launch star in Super Mario Galaxy.
Vasilis Bellos
Vasilis Bellos on 16 Dec 2025
That was my first thought as well!