Skyline

Animated City Skyline for React

A printed city along the foot of a section: halftone towers in the distance, ink towers in front with their windows knocked out, a flat sun and stippled trees. The first time it shows, the towers rise out of the ground and their windows switch on one by one. Give it your numbers and each one becomes a tower with a sign on its roof. Hover a tower and it lights up floor by floor. An elevated line can run across the front, with a train you can send from a button.

  • Hero
  • Chart
  • Canvas

Uses motion, clsx, tailwind-merge

Inspired by Citylines on vibrant.design

Open in ChatGPTOpen in Claude
seed7
lights0.60
trainfalse
headway9s
treestrue
idletrue
introtrue(replays the demo)
riseDuration0.55s(replays the demo)
stagger0.03s(replays the demo)
trainSpeed1.00×
flicker0.34s
<Skyline />

replays the demo when you let go.

Rent the flat where the evening light comes in

Westlight maps which way every window faces and how far each door is from the station, so you can pick a home for the hours you are actually in it.

Browse west-facing flats

1,284 flats · 9 lines · updated hourly

Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Skyline: A printed city along the foot of a section: halftone towers in the distance, ink towers in front with their windows knocked out, a flat sun and stippled trees. The first time it shows, the towers rise out of the ground and their windows switch on one by one. Give it your numbers and each one becomes a tower with a sign on its roof. Hover a tower and it lights up floor by floor. An elevated line can run across the front, with a train you can send from a button.
This demo, Hero City: A flat-hunting app's hero with the city along its foot. "Check the commute" sends a train across the line.

1. Install it:
   npx shadcn@latest add @keyframeui/skyline-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/skyline-demo.json instead.)
   It adds the demo, components/skyline-demo.tsx, and the component, components/ui/skyline.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <Skyline> on its own.

3. These props set its feel. Change them before you change the file:
   - seed (default 7): Reshapes the generated city, the far print, the clouds, the trees and which windows are lit. Any whole number.
   - lights (default 0.6): Share of the windows that are lit, from 0 to 1. A new value switches them on or off one at a time.
   - train (default false): An elevated line across the front, with trains crossing on it, behind the trees.
   - headway (default 9): Seconds between trains, counted from each departure. `0` runs them only when sent with `departures`.
   - trees (default true): Stippled trees along the foot of the scene, in front of everything.
   - idle (default true): Let the city live while it's on screen: a window switches on or off every so often and far dots blink.
   - intro (default true): Raise the towers and switch their lights on the first time the scene shows.
   - riseDuration (default 0.55): Seconds each tower takes to rise out of the ground the first time the scene shows.
   - stagger (default 0.03): Seconds between towers starting to rise, left to right. The spread stops growing after twelve towers.
   - trainSpeed (default 1): How fast trains cross, as a multiple of the default pace (about two seconds across 1280px). `2` is twice as fast.
   - flicker (default 0.34): Seconds between windows switching on or off while the city idles.

4. Don't change, in components/ui/skyline.tsx:
   - Towers, windows and glow are painted on the `near` canvas by one rAF loop that stops once nothing moves; React only re-renders for layout, signs and focus, never per frame.
   - Trains are two DOM layers moved by WAAPI `transform` alone, stacked between the `near` and `front` canvases, so they cross in front of the towers and behind the trees.
   - Windows switch on and off whole, one at a time in random order: lights, not fades. A far dot's blink repaints only its own cell.
   - Idle flicker, twinkles and the train timetable run only while the scene is on screen and the tab is visible.
   - Reduced motion draws the city lit at once, with nothing rising, blinking or crossing; hover still lights a tower and shows its sign, and a departure fades a train in standing still.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/skyline.md

Installation

Add the component on its own with the CLI, or copy the file in by hand.

The CLI writes components/ui/skyline.tsx and installs motion, clsx and tailwind-merge. It needs the @keyframeui registry in components.json; add it once per project.

npx shadcn@latest add @keyframeui/skyline

Examples

Finished sections built with Skyline. Each one installs whole, with the component included.

City Stats

Studio hours booked per city as towers, each with its share on a rooftop sign. Switch the year and the towers grow or shrink to it.

Where the studios were busiest

Hours booked on Roomtone in 2026, 17,830 h in all. Hover a city for its share.

Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Skyline: A printed city along the foot of a section: halftone towers in the distance, ink towers in front with their windows knocked out, a flat sun and stippled trees. The first time it shows, the towers rise out of the ground and their windows switch on one by one. Give it your numbers and each one becomes a tower with a sign on its roof. Hover a tower and it lights up floor by floor. An elevated line can run across the front, with a train you can send from a button.
This demo, City Stats: Studio hours booked per city as towers, each with its share on a rooftop sign. Switch the year and the towers grow or shrink to it.

1. Install it:
   npx shadcn@latest add @keyframeui/skyline-stats-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/skyline-stats-demo.json instead.)
   It adds the demo, components/skyline-stats-demo.tsx, and the component, components/ui/skyline.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <Skyline> on its own.

3. These props set its feel. Change them before you change the file:
   - seed (default 7): Reshapes the generated city, the far print, the clouds, the trees and which windows are lit. Any whole number.
   - lights (default 0.6): Share of the windows that are lit, from 0 to 1. A new value switches them on or off one at a time.
   - train (default false): An elevated line across the front, with trains crossing on it, behind the trees.
   - headway (default 9): Seconds between trains, counted from each departure. `0` runs them only when sent with `departures`.
   - trees (default true): Stippled trees along the foot of the scene, in front of everything.
   - idle (default true): Let the city live while it's on screen: a window switches on or off every so often and far dots blink.
   - intro (default true): Raise the towers and switch their lights on the first time the scene shows.
   - riseDuration (default 0.55): Seconds each tower takes to rise out of the ground the first time the scene shows.
   - stagger (default 0.03): Seconds between towers starting to rise, left to right. The spread stops growing after twelve towers.
   - trainSpeed (default 1): How fast trains cross, as a multiple of the default pace (about two seconds across 1280px). `2` is twice as fast.
   - flicker (default 0.34): Seconds between windows switching on or off while the city idles.

4. Don't change, in components/ui/skyline.tsx:
   - Towers, windows and glow are painted on the `near` canvas by one rAF loop that stops once nothing moves; React only re-renders for layout, signs and focus, never per frame.
   - Trains are two DOM layers moved by WAAPI `transform` alone, stacked between the `near` and `front` canvases, so they cross in front of the towers and behind the trees.
   - Windows switch on and off whole, one at a time in random order: lights, not fades. A far dot's blink repaints only its own cell.
   - Idle flicker, twinkles and the train timetable run only while the scene is on screen and the tab is visible.
   - Reduced motion draws the city lit at once, with nothing rising, blinking or crossing; hover still lights a tower and shows its sign, and a departure fades a train in standing still.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/skyline.md

Lights On

A late-night coworking club where every lit window is a member at a desk. Windows switch on and off as people come and go.

148of 240 desks lit

Lantern Hall stays open until four. Every window up there is a member working tonight.

Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Skyline: A printed city along the foot of a section: halftone towers in the distance, ink towers in front with their windows knocked out, a flat sun and stippled trees. The first time it shows, the towers rise out of the ground and their windows switch on one by one. Give it your numbers and each one becomes a tower with a sign on its roof. Hover a tower and it lights up floor by floor. An elevated line can run across the front, with a train you can send from a button.
This demo, Lights On: A late-night coworking club where every lit window is a member at a desk. Windows switch on and off as people come and go.

1. Install it:
   npx shadcn@latest add @keyframeui/skyline-lights-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/skyline-lights-demo.json instead.)
   It adds the demo, components/skyline-lights-demo.tsx, and the component, components/ui/skyline.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <Skyline> on its own.

3. These props set its feel. Change them before you change the file:
   - seed (default 7): Reshapes the generated city, the far print, the clouds, the trees and which windows are lit. Any whole number.
   - lights (default 0.6): Share of the windows that are lit, from 0 to 1. A new value switches them on or off one at a time.
   - train (default false): An elevated line across the front, with trains crossing on it, behind the trees.
   - headway (default 9): Seconds between trains, counted from each departure. `0` runs them only when sent with `departures`.
   - trees (default true): Stippled trees along the foot of the scene, in front of everything.
   - idle (default true): Let the city live while it's on screen: a window switches on or off every so often and far dots blink.
   - intro (default true): Raise the towers and switch their lights on the first time the scene shows.
   - riseDuration (default 0.55): Seconds each tower takes to rise out of the ground the first time the scene shows.
   - stagger (default 0.03): Seconds between towers starting to rise, left to right. The spread stops growing after twelve towers.
   - trainSpeed (default 1): How fast trains cross, as a multiple of the default pace (about two seconds across 1280px). `2` is twice as fast.
   - flicker (default 0.34): Seconds between windows switching on or off while the city idles.

4. Don't change, in components/ui/skyline.tsx:
   - Towers, windows and glow are painted on the `near` canvas by one rAF loop that stops once nothing moves; React only re-renders for layout, signs and focus, never per frame.
   - Trains are two DOM layers moved by WAAPI `transform` alone, stacked between the `near` and `front` canvases, so they cross in front of the towers and behind the trees.
   - Windows switch on and off whole, one at a time in random order: lights, not fades. A far dot's blink repaints only its own cell.
   - Idle flicker, twinkles and the train timetable run only while the scene is on screen and the tab is visible.
   - Reduced motion draws the city lit at once, with nothing rising, blinking or crossing; hover still lights a tower and shows its sign, and a departure fades a train in standing still.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/skyline.md

Props

towers

TypeSkylineTower[]

One tower per number, left to right, labelled under it: { label, value, display? }. The tallest stands nearly as tall as the scene, and display is what its sign says (the value with thousands separators by default). Leave it out for a city grown from seed.

seed

TypenumberDefault7

Reshapes the generated city, the far print, the clouds, the trees and which windows are lit. Any whole number.

lights

TypenumberDefault0.6

Share of the windows that are lit, from 0 to 1. A new value switches them on or off one at a time.

sun

Typeboolean | numberDefaulttrue

The sun disc behind the city. A number from 0 to 1 sets where it stands across the scene; the towers stand shorter around it.

train

TypebooleanDefaultfalse

An elevated line across the front, with trains crossing on it, behind the trees.

headway

TypenumberDefault9

Seconds between trains, counted from each departure. 0 runs them only when sent with departures.

departures

TypenumberDefault0

Bump this number to send a train at once, from a button for instance.

trees

TypebooleanDefaulttrue

Stippled trees along the foot of the scene, in front of everything.

signs

Type"hover" | "always"Default"hover"

With towers: show each tower's sign only while it's hovered, focused or tapped, or all the time.

idle

TypebooleanDefaulttrue

Let the city live while it's on screen: a window switches on or off every so often and far dots blink.

intro

TypebooleanDefaulttrue

Raise the towers and switch their lights on the first time the scene shows.

riseDuration

TypenumberDefault0.55

Seconds each tower takes to rise out of the ground the first time the scene shows.

stagger

TypenumberDefault0.03

Seconds between towers starting to rise, left to right. The spread stops growing after twelve towers.

trainSpeed

TypenumberDefault1

How fast trains cross, as a multiple of the default pace (about two seconds across 1280px). 2 is twice as fast.

flicker

TypenumberDefault0.34

Seconds between windows switching on or off while the city idles.

label

Typestring

What the scene shows, for screen readers. Without towers and without a label, the scene is decorative.

className

Typestring

Size and colors. Height comes from a class (h-80 by default). The text color is the ink; set --skyline-tint (the far print), --skyline-sun, --skyline-glow (a lit-up tower) and --skyline-light (lit windows) as CSS variables. Windows and the train are cut from the background behind the scene, or from --skyline-paper.