Batch 3 block idents: Paul's 5% notes (softer Sunday glow, richer tartan, Spooky contrast, subtler tracking line, Movie Night as a feature presentation)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
@@ -12,6 +12,8 @@ Each block folder has `review/<block>_contact_sheet.png`, `_final_still.png`, `_
|
||||
|---|---|---|---|
|
||||
| Sunday Gold | `orbit-gold-blocks/sunday-gold` | 173 KB | A ribbon of gold light flies in from the planet and writes *Sunday* (URW Z003 calligraphic italic) under the logo; two warm chimes. |
|
||||
| Scottish Comedy Night | `orbit-three-blocks/scottish-comedy-night` | 172 KB | The three hoops' moons line up; an original Orbit Three tartan (pink, yellow, cyan on purple, 2/2 twill, not any real sett) unrolls; THREE-style tiles drop in, the Y lands squint and the N bumps it straight. Sting: a little reed-like pentatonic run and a comic pop, no bagpipes. |
|
||||
| Vortex Movie Night | `vortex-blocks/movie-night` | 494 KB | The tunnel slows and dims, letterbox bars slide in with sprocket strips that slow to a stop, chrome MOVIE NIGHT with one glint. Larger than the others because the tunnel is so detailed, but under Vortex's own 1.27 MB ident. Sting: falling whoosh, low boom. |
|
||||
| Vortex Movie Night | `vortex-blocks/movie-night` | 458 KB | The tunnel slows, letterbox bars slide in with sprocket strips that slow to a stop, the house lights go down (the tunnel dims into a vignette), the logo sits centred, TONIGHT'S FEATURE PRESENTATION between fine rules, then a large chrome MOVIE NIGHT with one glint. Larger than the others because the tunnel is so detailed, but under Vortex's own 1.27 MB ident. Sting: falling whoosh, low boom. |
|
||||
| Retro Action | `orbit-2-blocks/retro-action` | 164 KB | A glowing road under Orbit 2's planet, headlights streaking past (light only), chrome RETRO ACTION with a star glint and one VHS tracking line. No scanner light, vehicles or show lettering. Sting: synth-bass stab and gated drum. Plays only when entering the booked block. |
|
||||
| Friday Spooky Teatime | `orbit-2-blocks/spooky-teatime` | 152 KB | A teacup drifts in, its steam becomes a small friendly ghost that says boo, three bats fly round the planet and perch on the wobbly title. Sting: teaspoon clink and a soft, silly wooo. |
|
||||
|
||||
**Paul's 5% notes, 3 November (2D only, no 3D re-render):** Sunday's glow softer and quieter; the tartan given woven grain, a light top fold, shade beneath and a soft drop shadow; SPOOKY TEATIME's outline heavier with a soft dark shadow per letter, and the bats a thin lilac rim; Retro Action's tracking line thinner, gentler and dimmer; Movie Night recomposed as a feature presentation (above).
|
||||
|
||||
@@ -131,7 +131,7 @@ class Block:
|
||||
face = ImageFont.truetype(SCRIPT, 132)
|
||||
ink = joined('Sunday', face, WHITE + (255,))
|
||||
word = gradient_fill(ink.getchannel('A'), [(0, (255, 236, 176)), (.5, (240, 192, 80)), (1, (176, 120, 32))])
|
||||
halo = glow(word, 6, 1.1)
|
||||
halo = glow(word, 5, .55) # Paul's 5% note: keep the glow soft and understated
|
||||
halo.alpha_composite(word)
|
||||
word = halo.rotate(5, Image.BICUBIC, expand=True)
|
||||
self.cache['sunday'] = word
|
||||
@@ -185,7 +185,10 @@ class Block:
|
||||
cut = band.copy(); cut.putalpha(ImageChops.multiply(band.getchannel('A'), mask))
|
||||
if u < 1: # the roll still to unfurl, as a darker rounded edge
|
||||
ImageDraw.Draw(cut).rounded_rectangle((edge - 16, -2, edge + 4, band.height + 2), 9, fill=(40, 12, 54, 255))
|
||||
place(canvas, cut.rotate(-2, Image.BICUBIC, expand=True), 50, 480)
|
||||
turned = cut.rotate(-2, Image.BICUBIC, expand=True)
|
||||
shadow = Image.new('RGBA', turned.size, (0, 0, 0, 0)); shadow.putalpha(turned.getchannel('A').point(lambda v: v * 150 // 255))
|
||||
place(canvas, shadow.filter(ImageFilter.GaussianBlur(6)), 54, 488)
|
||||
place(canvas, turned, 50, 480)
|
||||
tiles = self.cache.get('tiles') or self.cache.setdefault('tiles', tile_row('SCOTTISH COMEDY NIGHT'))
|
||||
squint = next(i for i, (c, *_) in enumerate(tiles) if c == 'Y')
|
||||
for i, (c, tile, x, base_rot) in enumerate(tiles):
|
||||
@@ -219,13 +222,36 @@ class Block:
|
||||
for k in range(-1, 42):
|
||||
x = k * 34 - travel % 34
|
||||
d.rounded_rectangle((x, y - 7, x + 18, y + 7), 3, fill=(26, 38, 56, 255))
|
||||
self.channel_logo(canvas, t, 1.2, 640, (54, 98))
|
||||
# Paul's 5% note, more of a feature presentation: the house lights go down (the tunnel dims into a vignette
|
||||
# inside the frame), the logo sits centred like a studio card, and a presentation line introduces the title.
|
||||
dim = kf(t, [(1.2, 0), (1.9, 1)], ease_io)
|
||||
if dim > 0:
|
||||
vignette = self.cache.get('vignette')
|
||||
if vignette is None:
|
||||
vignette = Image.new('L', (W, H), 0)
|
||||
ImageDraw.Draw(vignette).ellipse((-160, 40, W + 160, H - 40), fill=255)
|
||||
vignette = ImageChops.invert(vignette.filter(ImageFilter.GaussianBlur(120)))
|
||||
vignette = vignette.point(lambda v: 90 + v * 165 // 255)
|
||||
self.cache['vignette'] = vignette
|
||||
shade = Image.new('RGBA', (W, H), (0, 0, 0, 0)); shade.putalpha(vignette.point(lambda v: round(v * dim * .78)))
|
||||
inside = canvas.crop((0, bar, W, H - bar)); inside.alpha_composite(shade.crop((0, bar, W, H - bar))); canvas.paste(inside, (0, bar))
|
||||
logo = self.logo_at(500)
|
||||
place(canvas, logo, (W - logo.width) / 2 - 20, 112, kf(t, [(1.2, 0), (1.7, 1)], ease_io))
|
||||
line = self.cache.get('feature') or self.cache.setdefault('feature', text_layer("TONIGHT'S FEATURE PRESENTATION", font('barlow-regular.ttf', 24), (226, 236, 250, 255), 9))
|
||||
la = kf(t, [(1.6, 0), (2.0, 1)], ease_io)
|
||||
place(canvas, line, W / 2, 388, la, 'mt')
|
||||
reach = kf(t, [(1.7, 0), (2.2, 1)], ease_io) * 150
|
||||
if reach > 0:
|
||||
d = ImageDraw.Draw(canvas)
|
||||
for side in (-1, 1):
|
||||
x = W / 2 + side * (line.width / 2 + 22)
|
||||
d.line((x, 401, x + side * reach, 401), fill=(55, 223, 255, 200), width=2)
|
||||
title = self.cache.get('movie_title')
|
||||
if title is None:
|
||||
title = chrome('MOVIE NIGHT', font('anton-regular.ttf', 96), 6, [(0, (255, 255, 255)), (.42, (205, 220, 242)), (.5, (70, 92, 132)), (.56, (36, 52, 88)), (1, (196, 214, 240))])
|
||||
title = chrome('MOVIE NIGHT', font('anton-regular.ttf', 112), 8, [(0, (255, 255, 255)), (.42, (205, 220, 242)), (.5, (70, 92, 132)), (.56, (36, 52, 88)), (1, (196, 214, 240))])
|
||||
self.cache['movie_title'] = title
|
||||
a = kf(t, [(1.75, 0), (2.15, 1)], ease_io)
|
||||
place(canvas, glint(title, kf(t, [(2.45, 0), (2.9, 1)], ease_io)), 292, 400 + kf(t, [(1.75, 18), (2.15, 0)], ease_out), a)
|
||||
a = kf(t, [(1.85, 0), (2.25, 1)], ease_io)
|
||||
place(canvas, glint(title, kf(t, [(2.5, 0), (2.95, 1)], ease_io)), (W - title.width) / 2, 430 + kf(t, [(1.85, 18), (2.25, 0)], ease_out), a)
|
||||
|
||||
# ---- Retro Action: chrome title, a star glint, one VHS tracking line ----------------------------------------
|
||||
def draw_retro(self, canvas, pts, t):
|
||||
@@ -247,14 +273,14 @@ class Block:
|
||||
u = kf(t, [(2.55, 0), (2.95, 1)], lambda v: v)
|
||||
if 0 < u < 1:
|
||||
band_y = round(-30 + (H + 60) * u)
|
||||
strip = canvas.crop((0, max(0, band_y), W, min(H, band_y + 18)))
|
||||
strip = canvas.crop((0, max(0, band_y), W, min(H, band_y + 10)))
|
||||
if strip.height > 0:
|
||||
rng = random.Random(int(t * FPS))
|
||||
shifted = Image.new('RGBA', strip.size)
|
||||
for row in range(strip.height):
|
||||
line = strip.crop((0, row, W, row + 1))
|
||||
shifted.paste(line, (rng.randint(-14, 14), row))
|
||||
bright = Image.new('RGBA', strip.size, (255, 255, 255, 40))
|
||||
shifted.paste(line, (rng.randint(-5, 5), row))
|
||||
bright = Image.new('RGBA', strip.size, (255, 255, 255, 16)) # Paul's 5% note: subtle, so the road stays the star
|
||||
shifted.alpha_composite(bright)
|
||||
canvas.paste(shifted, (0, max(0, band_y)))
|
||||
|
||||
@@ -299,7 +325,7 @@ class Block:
|
||||
hop = kf(t, [(at, 26), (at + .16, -6), (at + .26, 0)], ease_out)
|
||||
turned = layer.rotate(w, Image.BICUBIC, expand=True)
|
||||
place(canvas, turned, tx + x - (turned.width - layer.width) / 2, ty + y + hop - (turned.height - layer.height) / 2, a)
|
||||
perches = [(tx + title[1][1] + 20, ty + title[1][2] - 6), (tx + title[6][1] + 30, ty + title[6][2] - 4), (tx + title[11][1] + 18, ty + title[11][2] - 6)]
|
||||
perches = [(tx + title[1][1] + 28, ty + title[1][2] + 6), (tx + title[6][1] + 38, ty + title[6][2] + 8), (tx + title[11][1] + 26, ty + title[11][2] + 6)] # on the letters' tops (they carry an 8 px shadow margin)
|
||||
p = pts['planet']
|
||||
px, py = (p['left'] + p['right']) / 2, (p['top'] + p['bottom']) / 2
|
||||
for i, (lx, ly) in enumerate(perches):
|
||||
@@ -337,7 +363,25 @@ def tartan(w, h, scale=3):
|
||||
for x in range(0, w, L):
|
||||
for y in range(0, h, L):
|
||||
cloth.paste(tile, (x, y))
|
||||
# Paul's 5% note, more depth: a woven grain, light catching the top fold and shade under it, like real cloth.
|
||||
rng = random.Random(9)
|
||||
grain = Image.new('L', (w, h))
|
||||
gd = ImageDraw.Draw(grain)
|
||||
for y in range(h):
|
||||
for x in range(0, w, 2):
|
||||
gd.point((x + (y % 2), y), fill=rng.randint(0, 255))
|
||||
grain = grain.filter(ImageFilter.GaussianBlur(.6))
|
||||
cloth = Image.blend(cloth, ImageChops.overlay(cloth, Image.merge('RGB', (grain, grain, grain))), .35)
|
||||
shade = Image.new('L', (1, h))
|
||||
for y in range(h):
|
||||
v = y / (h - 1)
|
||||
shade.putpixel((0, y), round(255 * (1.12 - .32 * v + .1 * math.sin(v * math.pi * 3))) if v < 1 else 200)
|
||||
cloth = ImageChops.multiply(cloth, Image.merge('RGB', [shade.resize((w, h))] * 3).point(lambda q: min(255, q)))
|
||||
highlight = Image.new('RGBA', (w, h))
|
||||
hd = ImageDraw.Draw(highlight)
|
||||
hd.rectangle((0, 4, w, 10), fill=(255, 255, 255, 46)); hd.rectangle((0, h - 12, w, h - 4), fill=(0, 0, 0, 70))
|
||||
cloth = cloth.convert('RGBA')
|
||||
cloth.alpha_composite(highlight)
|
||||
mask = Image.new('L', (w, h), 0)
|
||||
d = ImageDraw.Draw(mask)
|
||||
d.rectangle((0, 4, w, h - 4), fill=235)
|
||||
@@ -396,7 +440,15 @@ def make_ghost():
|
||||
|
||||
|
||||
def make_bat(flap):
|
||||
"""A tiny bat: wings beating (flap from -1 to 1), or folded when resting (None)."""
|
||||
"""A tiny bat with a thin lilac rim so it reads against the night: wings beating (flap from -1 to 1), or folded when resting (None)."""
|
||||
shape = draw_bat(flap)
|
||||
rim = Image.new('RGBA', shape.size, (170, 130, 230, 0))
|
||||
rim.putalpha(shape.getchannel('A').filter(ImageFilter.MaxFilter(3)))
|
||||
rim.alpha_composite(shape)
|
||||
return rim
|
||||
|
||||
|
||||
def draw_bat(flap):
|
||||
b = Image.new('RGBA', (48, 40))
|
||||
d = ImageDraw.Draw(b)
|
||||
body = (18, 10, 32)
|
||||
@@ -421,9 +473,16 @@ def wobbly(words):
|
||||
x += 26
|
||||
continue
|
||||
face = font('comfortaa.ttf', rng.choice((70, 74, 78)))
|
||||
letter = text_layer(c, face, (255, 154, 60, 255), 0, 4, (44, 16, 70))
|
||||
out.append((letter, x, rng.uniform(-8, 8), rng.uniform(-7, 7)))
|
||||
x += letter.width - 4
|
||||
letter = text_layer(c, face, (255, 154, 60, 255), 0, 6, (36, 12, 58))
|
||||
# Paul's 5% note: enough contrast on any screen, so a soft dark shadow sits behind each letter.
|
||||
pad = 8
|
||||
backed = Image.new('RGBA', (letter.width + 2 * pad, letter.height + 2 * pad))
|
||||
under = Image.new('RGBA', letter.size, (10, 4, 20, 0)); under.putalpha(letter.getchannel('A').point(lambda v: v * 200 // 255))
|
||||
backed.alpha_composite(under, (pad + 3, pad + 4)); backed = backed.filter(ImageFilter.GaussianBlur(4))
|
||||
backed.alpha_composite(letter, (pad, pad))
|
||||
letter = backed
|
||||
out.append((letter, x - pad, rng.uniform(-8, 8) - pad, rng.uniform(-7, 7)))
|
||||
x += letter.width - 4 - 2 * pad
|
||||
return out
|
||||
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
720
|
||||
],
|
||||
"crf": "34",
|
||||
"identBytes": 163575,
|
||||
"identBytes": 158642,
|
||||
"ssim": {
|
||||
"mean": 0.9608,
|
||||
"worst": 0.9525
|
||||
"mean": 0.9612,
|
||||
"worst": 0.9526
|
||||
},
|
||||
"guideAppBytes": 5656,
|
||||
"finalHoldStillForHalfSecond": true,
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
@@ -8,16 +8,16 @@
|
||||
720
|
||||
],
|
||||
"crf": "34",
|
||||
"identBytes": 151640,
|
||||
"identBytes": 160457,
|
||||
"ssim": {
|
||||
"mean": 0.9609,
|
||||
"mean": 0.9606,
|
||||
"worst": 0.9505
|
||||
},
|
||||
"guideAppBytes": 6348,
|
||||
"finalHoldStillForHalfSecond": true,
|
||||
"flash": {
|
||||
"frames": 120,
|
||||
"largestFrameChange": 0.0144,
|
||||
"largestFrameChange": 0.0145,
|
||||
"framesChangingOver10Percent": 0,
|
||||
"brightnessReversals": 0
|
||||
},
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 866 KiB After Width: | Height: | Size: 860 KiB |
@@ -8,9 +8,9 @@
|
||||
720
|
||||
],
|
||||
"crf": "34",
|
||||
"identBytes": 172581,
|
||||
"identBytes": 173193,
|
||||
"ssim": {
|
||||
"mean": 0.9581,
|
||||
"mean": 0.958,
|
||||
"worst": 0.9445
|
||||
},
|
||||
"guideAppBytes": 6150,
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 702 KiB After Width: | Height: | Size: 700 KiB |
@@ -8,16 +8,16 @@
|
||||
720
|
||||
],
|
||||
"crf": "34",
|
||||
"identBytes": 171705,
|
||||
"identBytes": 168879,
|
||||
"ssim": {
|
||||
"mean": 0.9653,
|
||||
"worst": 0.9564
|
||||
"mean": 0.9654,
|
||||
"worst": 0.9568
|
||||
},
|
||||
"guideAppBytes": 6604,
|
||||
"finalHoldStillForHalfSecond": true,
|
||||
"flash": {
|
||||
"frames": 120,
|
||||
"largestFrameChange": 0.0071,
|
||||
"largestFrameChange": 0.007,
|
||||
"framesChangingOver10Percent": 0,
|
||||
"brightnessReversals": 0
|
||||
},
|
||||
|
||||
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 826 KiB After Width: | Height: | Size: 844 KiB |
@@ -8,9 +8,9 @@
|
||||
720
|
||||
],
|
||||
"crf": "34",
|
||||
"identBytes": 494160,
|
||||
"identBytes": 458426,
|
||||
"ssim": {
|
||||
"mean": 0.9399,
|
||||
"mean": 0.9404,
|
||||
"worst": 0.8881
|
||||
},
|
||||
"guideAppBytes": 7108,
|
||||
|
||||
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 671 KiB After Width: | Height: | Size: 604 KiB |