from pathlib import Path import imageio.v2 as imageio import numpy as np from PIL import Image, ImageDraw, ImageFont ROOT = Path(__file__).resolve().parent OUT = ROOT / "lifechoice-demo.mp4" SIZE = (1280, 720) FPS = 12 def font(size: int, bold: bool = False): names = [ "C:/Windows/Fonts/arialbd.ttf" if bold else "C:/Windows/Fonts/arial.ttf", "C:/Windows/Fonts/segoeuib.ttf" if bold else "C:/Windows/Fonts/segoeui.ttf", ] for name in names: if Path(name).exists(): return ImageFont.truetype(name, size) return ImageFont.load_default() def title_card(title: str, subtitle: str) -> Image.Image: image = Image.new("RGB", SIZE, "#080812") draw = ImageDraw.Draw(image) draw.rounded_rectangle((80, 90, 1200, 630), radius=32, fill="#141128", outline="#7568ff", width=3) draw.text((130, 155), "BUILD SMALL HACKATHON", fill="#a99fff", font=font(24, True)) draw.text((130, 245), title, fill="white", font=font(62, True)) draw.multiline_text((130, 350), subtitle, fill="#d3cee7", font=font(30), spacing=12) return image def screenshot_card(filename: str, heading: str, caption: str, crop_top: int = 0) -> Image.Image: source = Image.open(ROOT / "screenshots" / filename).convert("RGB") source = source.crop((0, crop_top, source.width, min(source.height, crop_top + int(source.width * 0.56)))) source.thumbnail((1120, 540), Image.Resampling.LANCZOS) image = Image.new("RGB", SIZE, "#080812") draw = ImageDraw.Draw(image) draw.text((70, 34), heading, fill="white", font=font(34, True)) x = (SIZE[0] - source.width) // 2 image.paste(source, (x, 100)) draw.rounded_rectangle((60, 640, 1220, 700), radius=16, fill="#18152e") draw.text((90, 656), caption, fill="#eeeaff", font=font(22)) return image def write_frames(writer, image: Image.Image, seconds: float) -> None: frame = np.asarray(image) for _ in range(round(FPS * seconds)): writer.append_data(frame) def main() -> None: cards = [ (title_card("LifeChoice Simulator", "An LLM-powered causal decision simulator.\nQwen2.5-7B + Gradio + deterministic state."), 3.0), (screenshot_card("onboarding.png", "Start in seconds", "One concrete calibration answer takes you directly into the simulation."), 4.0), (screenshot_card("simulation.png", "Adaptive future nodes", "A 7B model generates bounded scenarios while deterministic state keeps score."), 4.0), (screenshot_card("cascade.png", "Choices return later", "Facts, obligations, and closed options make branches materially different.", 500), 4.0), (screenshot_card("report.png", "A report grounded in behavior", "Five metrics, three cascade moments, and a bounded causal ledger.", 500), 4.0), (title_card("Small model. Real state.", "7.616B scenario model. No 32B+ fallback model.\nBuilt with Codex for the Build Small Hackathon."), 3.0), ] with imageio.get_writer( OUT, fps=FPS, codec="libx264", quality=8, pixelformat="yuv420p", macro_block_size=1, ) as writer: for card, seconds in cards: write_frames(writer, card, seconds) print(OUT) if __name__ == "__main__": main()