Build a 3D city from live seismic data. Each building is an earthquake: height is magnitude, color is depth. Then fly the camera through it.
This script fetches live earthquake data, maps it to a 3D city, and then runs a cinematic camera flyover. The camera starts with a bird's eye view, sweeps the skyline, does a full 360-degree orbit, dives to street level, and pulls back to an overview.
-- city_flyover.lua — Cinematic 3D flyover of live earthquake data
--
-- Fetches real-time USGS earthquake data, builds a 3D city where:
-- Height = magnitude (log scale — big quakes tower over small ones)
-- Color = depth (shallow vs deep)
-- District = seismic network
-- Then flies the camera around the city.
ds.log("=== Earthquake City Flyover ===")
ds.log("Fetching live seismic data from USGS...")
-- Load last 30 days of earthquakes
ds.query([[
SELECT
round(mag, 2) AS magnitude,
round(depth, 1) AS depth_km,
net AS network,
magType AS mag_type,
place,
round(latitude, 2) AS lat,
round(longitude, 2) AS lon,
type AS event_type,
status
FROM read_csv_auto(
'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.csv'
)
WHERE mag IS NOT NULL AND mag > 0
ORDER BY mag DESC
]])
ds.log("Loaded " .. ds.data.row_count .. " earthquakes")
-- Configure the city mapping
ds.city.height = "magnitude"
ds.city.height_norm = "log" -- log scale: M7 towers over M2
ds.city.color = "depth_km"
ds.city.color_norm = "linear"
ds.city.district = "network" -- group by seismic network
ds.city.label = "place"
-- Trigger the city build (waits for completion, auto-switches to City view)
ds.city.rebuild()
-- Compute distances relative to city size
local extent = math.max(ds.city.extent_x, ds.city.extent_z)
local D = extent * 0.8 -- default fit distance
ds.log("City extent: " .. string.format("%.0f", extent) .. " units")
ds.log("")
ds.log("Starting flyover in 2 seconds...")
ds.sleep(2000)
-- =============================================
-- CINEMATIC FLYOVER
-- =============================================
-- 1. Start high up, looking down — the overview
ds.log("[camera] Bird's eye view")
ds.city.camera.set_mode("orbit")
ds.city.camera.orbit(-90, 70, D * 1.5, 1.5)
ds.city.camera.wait()
ds.sleep(1500)
-- 2. Swing around to see the skyline
ds.log("[camera] Skyline sweep")
ds.city.camera.orbit(0, 25, D, 2.0)
ds.city.camera.wait()
ds.sleep(1000)
-- 3. Continue orbiting — dramatic angle
ds.log("[camera] Dramatic angle")
ds.city.camera.orbit(90, 15, D * 0.75, 2.0)
ds.city.camera.wait()
ds.sleep(1000)
-- 4. Pull back and rise up for a wide shot
ds.log("[camera] Rising wide shot")
ds.city.camera.orbit(180, 45, D * 1.25, 2.5)
ds.city.camera.wait()
ds.sleep(1000)
-- 5. Slow 360° orbit — the hero shot
ds.log("[camera] 360° orbit")
for angle = 180, 540, 10 do
local a = angle % 360
if a > 180 then a = a - 360 end
ds.city.camera.orbit(a, 30, D * 0.9, 0.15)
ds.sleep(150)
end
-- 6. Dive low — street level view
ds.log("[camera] Street level dive")
ds.city.camera.orbit(45, 8, D * 0.4, 2.0)
ds.city.camera.wait()
ds.sleep(1500)
-- 7. Pull back to overview
ds.log("[camera] Return to overview")
ds.city.camera.fit(2.0)
ds.city.camera.wait()
ds.log("")
ds.log("=== Flyover complete ===")
ds.log("Try: ds.city.camera.orbit(0, 30, " .. string.format("%.0f", D) .. ", 1.0) to fly manually")
Height = magnitude (log scale), Color = depth in km, District = seismic network (groups buildings spatially).
7 shots: bird's eye, skyline sweep, dramatic angle, wide shot, 360-degree orbit, street-level dive, return to overview.
Change camera angles, speeds, and distances. Swap color to mag_type or district to event_type for different groupings.
Requires internet connection for the USGS API call. Works with both free and pro versions of ColumnLens.
Paste it into the Script Console and watch the flyover.
Or try the free version (up to 100 MB)