GPT-Image Monthly Theme Gallery: October 2026 Theme Is The Macabre

GPT-6.1-Sol, Max. 13m 1s

dancing_skeleton

Python code block, 16kB
"""Draw a seamless 480 × 480 skeleton dance using vector paths.

Requires Python 3.12+ and Pillow: python -m pip install Pillow
Run: python dancing_skeleton.py --output dancing_skeleton.gif
All artwork is generated here; no fonts, images, or other files are needed.
"""

from __future__ import annotations

import argparse
from math import atan2, cos, hypot, pi, sin, sqrt, tau
from pathlib import Path

from PIL import Image, ImageDraw

type Point = tuple[float, float]
type Transform = tuple[float, float, float, float, float, float]

SIZE = 480
SCALE = 3
FRAMES = 200
FRAME_MS = 20
BACKGROUND = "#211c36"
HALO = "#2b2547"
OUTLINE = "#51415c"
BONE = "#fff2d4"
SHADE = "#d8bea0"
HIGHLIGHT = "#fffbed"
INK = "#332b45"
CORAL = "#f69b9d"
MINT = "#90d9c5"
IDENTITY: Transform = (1, 0, 0, 1, 0, 0)


def transform(x: float, y: float, angle: float = 0) -> Transform:
    c, s = cos(angle), sin(angle)
    return c, s, -s, c, x, y


def apply(point: Point, matrix: Transform) -> Point:
    x, y = point
    a, b, c, d, e, f = matrix
    return a * x + c * y + e, b * x + d * y + f


def curve(*points: Point) -> list[Point]:
    """Sample chained cubic Bézier segments for supersampled vector drawing."""
    result = [points[0]]
    for i in range(1, len(points), 3):
        p0, p1, p2, p3 = points[i - 1 : i + 3]
        for step in range(1, 17):
            t = step / 16
            u = 1 - t
            result.append(
                (
                    u**3 * p0[0]
                    + 3 * u * u * t * p1[0]
                    + 3 * u * t * t * p2[0]
                    + t**3 * p3[0],
                    u**3 * p0[1]
                    + 3 * u * u * t * p1[1]
                    + 3 * u * t * t * p2[1]
                    + t**3 * p3[1],
                )
            )
    return result


class Canvas:
    def __init__(self, image: Image.Image) -> None:
        self.image = image
        self.draw = ImageDraw.Draw(image)

    def path(
        self,
        points: list[Point],
        fill: str | None = None,
        stroke: str | None = None,
        width: float = 1,
        matrix: Transform = IDENTITY,
    ) -> None:
        pixels = [tuple(v * SCALE for v in apply(p, matrix)) for p in points]
        if fill:
            self.draw.polygon(pixels, fill=fill)
        if stroke:
            if fill:
                pixels.append(pixels[0])
            self.draw.line(
                pixels, fill=stroke, width=round(width * SCALE), joint="curve"
            )
            radius = width * SCALE / 2
            for x, y in (pixels[0], pixels[-1]):
                self.draw.ellipse(
                    (x - radius, y - radius, x + radius, y + radius), fill=stroke
                )

    def ellipse(
        self,
        center: Point,
        radii: Point,
        fill: str,
        stroke: str | None = None,
        width: float = 1,
        matrix: Transform = IDENTITY,
    ) -> None:
        x, y = center
        rx, ry = radii
        self.path(
            [
                (x + rx * cos(tau * i / 64), y + ry * sin(tau * i / 64))
                for i in range(64)
            ],
            fill,
            stroke,
            width,
            matrix,
        )


def bone(canvas: Canvas, start: Point, end: Point, radius: float = 6) -> None:
    length = hypot(end[0] - start[0], end[1] - start[1])
    matrix = transform(*start, atan2(end[1] - start[1], end[0] - start[0]) - pi / 2)
    r, w = radius, radius * 0.55
    taper = min(r * 2, length * 0.35)
    silhouette = curve(
        (-r, 0),
        (-r, -r),
        (-r * 0.25, -r * 1.3),
        (0, -r * 0.65),
        (r * 0.25, -r * 1.3),
        (r, -r),
        (r, 0),
        (r, r),
        (w, r),
        (w, taper),
        (w, length * 0.45),
        (w, length * 0.55),
        (w, length - taper),
        (w, length - r),
        (r, length - r),
        (r, length),
        (r, length + r),
        (r * 0.25, length + r * 1.3),
        (0, length + r * 0.65),
        (-r * 0.25, length + r * 1.3),
        (-r, length + r),
        (-r, length),
        (-r, length - r),
        (-w, length - r),
        (-w, length - taper),
        (-w, length * 0.55),
        (-w, length * 0.45),
        (-w, taper),
        (-w, r),
        (-r, r),
        (-r, 0),
    )
    canvas.path(silhouette, BONE, OUTLINE, 1.8, matrix)
    canvas.path(
        [(w * 0.5, r * 1.5), (w * 0.5, length - r)],
        stroke=SHADE,
        width=1.5,
        matrix=matrix,
    )
    canvas.path(
        [(-w * 0.55, r * 1.5), (-w * 0.55, length * 0.62)],
        stroke=HIGHLIGHT,
        width=1.3,
        matrix=matrix,
    )


def paired_bones(canvas: Canvas, start: Point, end: Point) -> None:
    dx, dy = end[0] - start[0], end[1] - start[1]
    length = hypot(dx, dy)
    for sign in (-1, 1):
        offset = (-dy / length * 3.2 * sign, dx / length * 3.2 * sign)
        bone(
            canvas,
            (start[0] + offset[0], start[1] + offset[1]),
            (end[0] + offset[0], end[1] + offset[1]),
            3.3,
        )


def joint(start: Point, end: Point, first: float, second: float, bend: int) -> Point:
    """Two-bone inverse kinematics keeps each limb connected and rigid."""
    dx, dy = end[0] - start[0], end[1] - start[1]
    distance = hypot(dx, dy)
    along = (first * first - second * second + distance * distance) / (2 * distance)
    height = sqrt(max(0, first * first - along * along))
    return (
        start[0] + dx / distance * along - bend * dy / distance * height,
        start[1] + dy / distance * along + bend * dx / distance * height,
    )


def hand(canvas: Canvas, wrist: Point, side: int, phase: float) -> None:
    matrix = transform(*wrist, side * 0.30 + 0.16 * sin(4 * phase + side))
    for x, spread, length in ((-6, -10, 15), (-2, -4, 22), (3, 4, 23), (7, 11, 17)):
        start = apply((x, -5), matrix)
        middle = apply((x + spread * 0.45, -5 - length * 0.55), matrix)
        end = apply((x + spread, -5 - length), matrix)
        bone(canvas, start, middle, 2.4)
        bone(canvas, middle, end, 2.1)
    thumb = [
        apply(p, matrix)
        for p in ((-side * 5, 2), (-side * 13, -1), (-side * 17, -7))
    ]
    bone(canvas, thumb[0], thumb[1], 2.7)
    bone(canvas, thumb[1], thumb[2], 2.3)
    canvas.ellipse((0, 0), (8, 10), BONE, OUTLINE, 1.8, matrix)
    canvas.ellipse((1.4, 1.3), (3, 4.3), SHADE, matrix=matrix)


def foot(canvas: Canvas, ankle: Point, side: int, lift: float) -> None:
    angle = side * (-0.10 - 0.40 * lift)
    c, s = cos(angle), sin(angle)
    matrix: Transform = (side * c, side * s, -s, c, *ankle)
    canvas.ellipse((-2, 3), (7, 7.5), BONE, OUTLINE, 1.9, matrix)
    for y, length in ((-1, 30), (5, 34), (11, 29)):
        bone(canvas, apply((1, 3), matrix), apply((length - 7, y), matrix), 2.7)
        bone(canvas, apply((length - 7, y), matrix), apply((length, y + 1), matrix), 3)


def torso(canvas: Canvas, matrix: Transform) -> None:
    for y in (-112, -105, -98, -42, -34, -26, -18):
        canvas.ellipse((0, y), (6, 3.8), BONE, OUTLINE, 1.4, matrix)
    for y, width in ((-90, 32), (-77, 31), (-64, 27), (-51, 21)):
        for side in (-1, 1):
            rib = curve(
                (0, y),
                (side * 10, y - 6),
                (side * width, y - 5),
                (side * width, y + 1),
                (side * width, y + 8),
                (side * 13, y + 13),
                (side * 2, y + 10),
            )
            canvas.path(rib, stroke=OUTLINE, width=7.8, matrix=matrix)
            canvas.path(rib, stroke=BONE, width=4.8, matrix=matrix)
    bone(canvas, apply((0, -94), matrix), apply((0, -47), matrix), 4.5)
    for side in (-1, 1):
        clavicle = curve(
            (0, -96), (side * 10, -105), (side * 22, -102), (side * 33, -94)
        )
        canvas.path(clavicle, stroke=OUTLINE, width=8, matrix=matrix)
        canvas.path(clavicle, stroke=BONE, width=5, matrix=matrix)
    pelvis = curve(
        (0, -7),
        (-5, -13),
        (-27, -17),
        (-31, -7),
        (-35, 8),
        (-19, 22),
        (-10, 15),
        (-6, 12),
        (-3, 10),
        (0, 8),
        (3, 10),
        (6, 12),
        (10, 15),
        (19, 22),
        (35, 8),
        (31, -7),
        (27, -17),
        (5, -13),
        (0, -7),
    )
    canvas.path(pelvis, BONE, OUTLINE, 2.3, matrix)
    for side in (-1, 1):
        canvas.ellipse((side * 19, 2), (6.5, 8), HALO, OUTLINE, 1.6, matrix)
    canvas.ellipse((0, -2), (5, 8), SHADE, matrix=matrix)
    for side in (-1, 1):
        canvas.path(
            curve(
                (side * 2, -106),
                (side * 9, -114),
                (side * 15, -114),
                (side * 14, -108),
                (side * 16, -99),
                (side * 11, -99),
                (side * 2, -103),
            ),
            CORAL,
            OUTLINE,
            1.8,
            matrix,
        )
    canvas.ellipse((0, -105), (4, 4.5), "#e7828b", OUTLINE, 1.5, matrix)


def skull(canvas: Canvas, center: Point, tilt: float, phase: float) -> None:
    c, s = cos(tilt) * 1.04, sin(tilt) * 1.04
    matrix: Transform = (c, s, -s, c, *center)
    wink = max(0, cos(phase - pi * 1.35)) ** 36
    silhouette = curve(
        (0, -53),
        (-37, -54),
        (-54, -31),
        (-54, -7),
        (-55, 10),
        (-49, 20),
        (-37, 23),
        (-34, 28),
        (-36, 38),
        (-25, 42),
        (-13, 47),
        (13, 47),
        (25, 42),
        (36, 38),
        (34, 28),
        (37, 23),
        (49, 20),
        (55, 10),
        (54, -7),
        (54, -31),
        (37, -54),
        (0, -53),
    )
    canvas.path(silhouette, BONE, OUTLINE, 3, matrix)
    canvas.path(
        curve((-38, -24), (-29, -43), (-6, -47), (13, -43)),
        stroke=HIGHLIGHT,
        width=5,
        matrix=matrix,
    )
    canvas.path(
        curve((-25, 39), (-10, 44), (10, 44), (25, 39)),
        stroke=SHADE,
        width=3,
        matrix=matrix,
    )
    for side in (-1, 1):
        opening = 1 - 0.90 * wink if side == -1 else 1
        canvas.ellipse((side * 22, -6), (14, 18.5 * opening), INK, matrix=matrix)
        canvas.ellipse(
            (side * 22 - 3, -6 - 6 * opening),
            (4, 5.5 * opening),
            HIGHLIGHT,
            matrix=matrix,
        )
        canvas.ellipse(
            (side * 22 + 4, -6 + 5 * opening),
            (1.7, 2.2 * opening),
            "#aea0bd",
            matrix=matrix,
        )
        canvas.ellipse((side * 38, 17), (7, 3.8), CORAL, matrix=matrix)
    nose = curve(
        (0, 9),
        (-3, 6),
        (-7, 10),
        (-4, 13),
        (-2, 16),
        (-1, 17),
        (0, 18),
        (1, 17),
        (2, 16),
        (4, 13),
        (7, 10),
        (3, 6),
        (0, 9),
    )
    canvas.path(nose, INK, matrix=matrix)
    smile = curve((-25, 24), (-14, 36), (14, 36), (25, 24))
    canvas.path(smile, stroke=OUTLINE, width=2.5, matrix=matrix)
    for x in (-16, -6, 6, 16):
        y = 32 - (abs(x) / 16) ** 2 * 3
        canvas.path([(x, y - 4), (x, y + 3)], stroke=OUTLINE, width=1.7, matrix=matrix)


def sparkle(canvas: Canvas, center: Point, radius: float, color: str) -> None:
    x, y = center
    canvas.path(
        [
            (x, y - radius),
            (x + radius * 0.24, y - radius * 0.24),
            (x + radius, y),
            (x + radius * 0.24, y + radius * 0.24),
            (x, y + radius),
            (x - radius * 0.24, y + radius * 0.24),
            (x - radius, y),
            (x - radius * 0.24, y - radius * 0.24),
        ],
        color,
    )


def background() -> Image.Image:
    canvas = Canvas(Image.new("RGB", (SIZE * SCALE, SIZE * SCALE), BACKGROUND))
    canvas.ellipse((240, 242), (179, 179), "#25203e")
    canvas.ellipse((240, 242), (157, 157), HALO)
    canvas.ellipse((240, 422), (109, 19), "#18152b")
    canvas.ellipse((240, 420), (91, 11), "#322743")
    return canvas.image


BASE = background()


def render_frame(phase: float) -> Image.Image:
    """Every pose channel is periodic; phase 0 and phase 2π are identical."""
    canvas = Canvas(BASE.copy())
    sway = sin(2 * phase)
    bounce = 3.5 * cos(4 * phase) + 1.3 * cos(8 * phase)
    body = transform(240 + 14 * sway, 297 + bounce, 0.075 * sway + 0.02 * sin(phase))
    canvas.ellipse((240 + 8 * sway, 418), (53 - 4 * cos(4 * phase), 5.5), "#141225")
    for i, (x, y, radius, color) in enumerate(
        (
            (111, 105, 7, MINT),
            (361, 106, 6, CORAL),
            (79, 276, 5, CORAL),
            (393, 267, 7, MINT),
            (120, 341, 3, MINT),
            (360, 348, 3, CORAL),
        )
    ):
        sparkle(
            canvas,
            (x, y + 3 * sin(2 * phase + i)),
            radius * (0.82 + 0.18 * sin(4 * phase + i)),
            color,
        )
    for side in (-1, 1):
        lift = max(0, -side * sway) ** 2
        hip = apply((side * 18, 6), body)
        ankle = (240 + side * (32 + 15 * lift), 404 - 30 * lift)
        knee = joint(hip, ankle, 57, 59, -side)
        bone(canvas, hip, knee, 7)
        paired_bones(canvas, knee, ankle)
        canvas.ellipse(knee, (5, 5), BONE, OUTLINE, 1.7)
        foot(canvas, ankle, side, lift)
    for side in (-1, 1):
        shoulder = apply((side * 34, -94), body)
        wrist = apply(
            (
                side * (104 + 6 * sway),
                -117 + side * 18 * cos(2 * phase) - 6 * sin(4 * phase) - 7 * sin(phase),
            ),
            body,
        )
        elbow = joint(shoulder, wrist, 46, 48, side)
        bone(canvas, shoulder, elbow, 6.3)
        paired_bones(canvas, elbow, wrist)
        canvas.ellipse(elbow, (4.6, 4.6), BONE, OUTLINE, 1.7)
        hand(canvas, wrist, side, phase)
    torso(canvas, body)
    skull(
        canvas,
        apply((0, -155), body),
        -0.11 * sway + 0.025 * sin(4 * phase) + 0.035 * sin(phase),
        phase,
    )
    return canvas.image.resize((SIZE, SIZE), Image.Resampling.LANCZOS)


def save_gif(output: Path) -> None:
    frames = [render_frame(tau * i / FRAMES) for i in range(FRAMES)]
    # Reserve artwork colors, then use the remaining palette for antialiasing.
    anchors = (
        BACKGROUND,
        HALO,
        OUTLINE,
        BONE,
        SHADE,
        HIGHLIGHT,
        INK,
        CORAL,
        MINT,
        "#aea0bd",
        "#e7828b",
        "#25203e",
        "#18152b",
        "#322743",
        "#141225",
    )
    colors = [tuple(bytes.fromhex(color[1:])) for color in anchors]
    backdrop = bytes.fromhex(HALO[1:])
    for color in (MINT, CORAL):
        foreground = bytes.fromhex(color[1:])
        colors.extend(
            tuple(round(a * t + b * (1 - t)) for a, b in zip(foreground, backdrop))
            for t in (0.25, 0.5, 0.75)
        )
    sample = Image.new("RGB", (SIZE * 4, SIZE * 2))
    for i in range(8):
        sample.paste(frames[i * FRAMES // 8], ((i % 4) * SIZE, (i // 4) * SIZE))
    count = 256 - len(colors)
    adaptive = sample.quantize(colors=count, method=Image.Quantize.MEDIANCUT)
    palette = Image.new("P", (1, 1))
    palette.putpalette(
        [channel for color in colors for channel in color]
        + adaptive.getpalette()[: count * 3]
    )
    indexed = [
        frame.quantize(palette=palette, dither=Image.Dither.NONE) for frame in frames
    ]
    indexed[0].save(
        output,
        save_all=True,
        append_images=indexed[1:],
        duration=FRAME_MS,
        loop=0,
        disposal=1,
        optimize=False,
    )
    print(
        f"Saved {output}: {SIZE}×{SIZE}, {FRAMES} frames, "
        f"{FRAMES * FRAME_MS / 1000:g} s, seamless loop"
    )


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--output", type=Path, default=Path("dancing_skeleton.gif"))
    args = parser.parse_args()
    save_gif(args.output)


if __name__ == "__main__":
    main()

@_j that is super cute and so sharp. I enjoy you so much.

forget simon willisons pelicans, we should have Jay’s skeletons!

Everyone has Dots on the brain. I predict we will see many more multi frame animation. It’s really cool to watch.


witch_cat_smooth_transparent

@LarisaHaster ‘s watch gif was amazing as gifs go.



Nutella Murder on Isle 4

The macabre side: diabetes after so many takes

Giants with Tiny Hands


Tiny People with Giant Hands

Rock & Roll

Inspired by @jeffvpace

And I love how GPT Image just brought in “Final Cut” - I mean if I was putting together a band like this, that would be EXACTLY the name i would give them!

Looks like you got an all star cast :grinning_face:

The beast inside.


The beast I feed.


Tales of the Unknown

You should isolate the Boo it would be cool. I added a !