presque.cool
Back to projects
Star Wars Countdown icon

Star Wars Countdown

Sep 2021 App
Visit project

A Star Wars-themed countdown timer with a Canvas visualization inspired by the Death Star circumnavigation scene from the Battle of Yavin.


Le pitch

Dans la Bataille de Yavin, un cadran sur les écrans impériaux montre la Death Star qui s’aligne lentement sur sa cible. Cette image — un compte à rebours orbital, tendu, inexorable — m’a toujours fasciné. Star Wars Countdown la transforme en outil : un compte à rebours visuel vers les prochains événements Star Wars, où le cadran Canvas reproduit cette circumnavigation iconique.

Essayer : presque.cool/starwarscountdown/

Durée ~2-3 jours par version
Période Sept 2021 → Déc 2025
Stack
Preact TypeScript Canvas Zustand
Statut ✓ En production

Pourquoi ce projet

Être fan de Star Wars, c’est vivre dans l’attente permanente. Le prochain film, la prochaine série, la prochaine Celebration. Les countdowns génériques existent par dizaines — mais aucun ne ressemblait à Star Wars. Je voulais quelque chose qui donne la même tension que ce cadran impérial : le temps qui s’écoule, la cible qui approche, et cette bascule au vert quand le jour arrive enfin.

Le projet est né en septembre 2021, pendant l’attente de la série Obi-Wan Kenobi. Depuis, il est mis à jour chaque année avec les nouvelles dates. C’est devenu un petit rituel : chaque annonce officielle, je mets à jour le JSON.


La circumnavigation de la Death Star

La visualisation n’est pas un réticule de visée — c’est une circumnavigation. Elle s’inspire directement de la scène dans l’Épisode IV où la Death Star s’aligne sur la planète Yavin pendant la Bataille de Yavin. Ce cadran de progression orbitale, visible sur les écrans de contrôle impériaux, est l’une des images les plus reconnaissables de la saga.

Le rendu Canvas superpose plusieurs couches :

  • Un disque central blanc de 80 px de rayon
  • Deux secteurs angulaires — un vert fixe (la cible, ~99°) et un rouge qui grandit avec le temps écoulé (l’approche)
  • 10 couronnes concentriques composées d’arcs aléatoires, avec des épaisseurs et couleurs variées (dégradés de rouge sombre)
  • Des lignes de fond et indicateurs clignotants — points verts, rouges et jaunes qui simulent les capteurs d’un écran de contrôle

Quand la date est atteinte, tout passe au vert — la cible est acquise.


Le rendu Canvas

Canvas plutôt que SVG ou CSS s’est imposé naturellement. L’animation nécessite de redessiner l’intégralité du cadran à chaque frame — les arcs aléatoires, les indicateurs clignotants, le secteur rouge en croissance. Canvas est le bon outil pour ce type de rendu intensif.

Le pipeline de rendu est entièrement composé de fonctions pures : chaque couche (fond, secteurs, couronnes, lignes) est une fonction qui reçoit le contexte Canvas et les paramètres de l’état courant. Aucun effet de bord en dehors du dessin lui-même.

Les configurations des couronnes (rayons, épaisseurs, couleurs) et des lignes de fond sont pré-définies dans des fichiers de données séparés (shell-sets.ts, line-sets.ts), ce qui rend le réglage visuel indépendant de la logique de rendu.


L’optimisation des performances

Un problème de performance a été constaté sur mobile : redessiner le cadran complet à 60 fps drainait la batterie. La solution : un frame-skip — le Canvas ne se redessine que toutes les 30 frames (~500 ms à 60 fps). L’animation conserve un mouvement perceptible sans consommer de ressources inutiles.

Deux niveaux de contrôle coexistent :

  • Préférence systèmeprefers-reduced-motion: reduce désactive les animations automatiquement
  • Toggle utilisateur — un réglage dans les paramètres permet de forcer l’activation ou la désactivation

Quand les animations sont désactivées, le Canvas effectue un seul rendu au montage et ne se redessine qu’au changement d’état. Le countdown lui-même s’arrête (clearInterval) une fois la date atteinte.


Le routing par date

Chaque événement a sa propre URL : /starwarscountdown/2027-12-17. Pas de bibliothèque de routing — juste window.history.pushState et un listener popstate. Le minimum nécessaire.

Si l’URL est invalide, le site redirige silencieusement (replaceState) vers le prochain événement à venir. Si tous les événements sont passés, il affiche le plus récent.

Ce choix s’inscrit dans une volonté de réduire les dépendances au minimum. Le projet n’a besoin que de Preact, Zustand et Lucide — tout le reste est fait maison.


Les événements

Plus de 40 événements sont référencés dans un fichier JSON maintenu manuellement : films, séries, jeux, célébrations, sorties de parcs d’attraction. Les dates proviennent de StarWars.com et de l’actualité Star Wars que je suis régulièrement.

Le sélecteur dropdown permet de naviguer entre tous les événements. Le site s’ouvre automatiquement sur le prochain événement à venir.


Stack technique

  • Preact + TypeScript — Composants fonctionnels, hooks custom, typage strict
  • Vite + SWC — Build rapide, HMR
  • Canvas API — Rendu du cadran, fonctions pures, DPI-aware
  • Zustand — State persisté (thème, langue, animations), rehydratation validée
  • Tailwind CSS — Variables CSS custom, dark/light/system mode
  • Lucide — Icônes (ChevronDown, Settings, X)
  • i18n maison — Français/anglais, type-safe avec clés en dot notation
  • Compression — Brotli + Gzip sur les assets
  • CSP — Content-Security-Policy avec hashes auto-générés

Évolutions

DateVersionDescription
Sept 20211.0Version initiale — Vanilla JS, countdown basique
Mai 20221.1Mise à jour des événements
Mai 20231.2Mise à jour des événements
Mai 20241.3Mise à jour des événements
Mai 20251.4Mise à jour des événements
Déc 20252.0Refonte Preact + TypeScript, amélioration graphique, a11y, i18n

Bilan

Durée : 2-3 jours par version (septembre 2021, refondu en décembre 2025).

Ce que j’ai appris :

  • Que reproduire une référence visuelle précise (les écrans impériaux) force à itérer sur des détails qu’on ignorerait autrement — les épaisseurs de traits, les opacités, les couleurs exactes des indicateurs clignotants
  • Que le frame-skip (redraw toutes les 30 frames) est la bonne granularité pour Canvas sur mobile — assez rapide pour paraître animé, assez lent pour épargner la batterie
  • Que la v2 (Preact) a transformé un fichier monolithique ingérable en une architecture de hooks et fonctions pures — le code est passé de “ça marche” à “je peux le modifier”

Ce que je referais pareil :

  • Le routing maison par date sans bibliothèque — 20 lignes de code, zéro dépendance, tout ce qu’il faut
  • Le maintien des événements à la main — je suis l’actu Star Wars de toute façon, et la curation éditoriale donne plus de valeur qu’un scraper automatique

Ce que je changerais :

  • Explorer une version app mobile native pour envoyer des notifications avant les événements
  • Trouver une typographie plus expressive que le monospace actuel — quelque chose qui évoque mieux l’univers Star Wars sans tomber dans le pastiche

The pitch

In the Battle of Yavin, a dial on the Imperial control screens shows the Death Star slowly aligning with its target. That image — an orbital countdown, tense, inexorable — has always fascinated me. Star Wars Countdown turns it into a tool: a visual countdown to upcoming Star Wars events, where the Canvas dial recreates that iconic circumnavigation.

Try it: presque.cool/starwarscountdown/

Duration ~2-3 days per version
Period Sep 2021 → Dec 2025
Stack
Preact TypeScript Canvas Zustand
Status ✓ In production

Why this project

Being a Star Wars fan means living in permanent anticipation. The next movie, the next series, the next Celebration. Generic countdowns exist by the dozen — but none of them looked like Star Wars. I wanted something that conveyed the same tension as that Imperial dial: time ticking away, the target approaching, and that switch to green when the day finally arrives.

The project was born in September 2021, during the wait for the Obi-Wan Kenobi series. Since then, it’s been updated every year with new dates. It’s become a small ritual: every official announcement, I update the JSON.


The Death Star circumnavigation

The visualization isn’t a targeting reticle — it’s a circumnavigation display. It draws directly from the scene in Episode IV where the Death Star aligns with the planet Yavin during the Battle of Yavin. This orbital progress dial, visible on Imperial control screens, is one of the saga’s most recognizable images.

The Canvas rendering layers several elements:

  • A central disc — white, 80px radius
  • Two angular sectors — a fixed green one (the target, ~99°) and a red one that grows with elapsed time (the approach)
  • 10 concentric rings made of randomized arcs, with varying stroke widths and colors (dark red gradients)
  • Background lines and blinking indicators — green, red, and yellow dots simulating a control screen’s sensors

When the date is reached, everything turns green — target acquired.


Canvas rendering

Canvas over SVG or CSS was a natural choice. The animation requires redrawing the entire dial every frame — the randomized arcs, blinking indicators, the growing red sector. Canvas is the right tool for this type of intensive rendering.

The rendering pipeline is entirely composed of pure functions: each layer (background, sectors, rings, lines) is a function that receives the Canvas context and current state parameters. No side effects beyond drawing itself.

Ring configurations (radii, stroke widths, colors) and background line definitions are pre-defined in separate data files (shell-sets.ts, line-sets.ts), making visual tuning independent from rendering logic.


Performance optimization

A performance issue was observed on mobile: redrawing the full dial at 60 fps was draining the battery. The solution: frame-skipping — the Canvas only redraws every 30 frames (~500ms at 60fps). The animation maintains perceptible movement without consuming unnecessary resources.

Two levels of control coexist:

  • System preferenceprefers-reduced-motion: reduce disables animations automatically
  • User toggle — a setting allows forcing animations on or off

When animations are disabled, the Canvas performs a single render on mount and only redraws on state change. The countdown itself stops (clearInterval) once the date is reached.


Date-based routing

Each event has its own URL: /starwarscountdown/2027-12-17. No routing library — just window.history.pushState and a popstate listener. The bare minimum.

If the URL is invalid, the site silently redirects (replaceState) to the next upcoming event. If all events have passed, it shows the most recent one.

This choice reflects a desire to keep dependencies to a minimum. The project only needs Preact, Zustand, and Lucide — everything else is handmade.


The events

Over 40 events are listed in a manually maintained JSON file: movies, series, games, celebrations, theme park releases. Dates come from StarWars.com and from following Star Wars news regularly.

The dropdown selector allows navigating between all events. The site automatically opens on the next upcoming event.


Tech stack

  • Preact + TypeScript — Functional components, custom hooks, strict typing
  • Vite + SWC — Fast builds, HMR
  • Canvas API — Dial rendering, pure functions, DPI-aware
  • Zustand — Persisted state (theme, language, animations), validated rehydration
  • Tailwind CSS — Custom CSS variables, dark/light/system mode
  • Lucide — Icons (ChevronDown, Settings, X)
  • i18n custom — French/English, type-safe with dot notation keys
  • Compression — Brotli + Gzip on assets
  • CSP — Content-Security-Policy with auto-generated hashes

Timeline

DateVersionDescription
Sep 20211.0Initial version — Vanilla JS, basic countdown
May 20221.1Events update
May 20231.2Events update
May 20241.3Events update
May 20251.4Events update
Dec 20252.0Preact + TypeScript rewrite, visual improvements, a11y, i18n

Takeaways

Duration: 2-3 days per version (September 2021, rewritten December 2025).

What I learned:

  • That reproducing a precise visual reference (Imperial control screens) forces you to iterate on details you’d otherwise ignore — stroke widths, opacities, exact blinking indicator colors
  • That frame-skipping (redraw every 30 frames) is the right granularity for Canvas on mobile — fast enough to appear animated, slow enough to spare the battery
  • That v2 (Preact) transformed an unmanageable monolithic file into a hooks-and-pure-functions architecture — the code went from “it works” to “I can modify it”

What I’d do the same:

  • Handmade date-based routing without a library — 20 lines of code, zero dependencies, everything needed
  • Manually maintaining events — I follow Star Wars news anyway, and editorial curation provides more value than an automatic scraper

What I’d change:

  • Explore a native mobile app version to send notifications before events
  • Find a more expressive typeface than the current monospace — something that better evokes the Star Wars universe without falling into pastiche

Settings

Language
Theme
Privacy Policy