Commute Time Customization

New in v1.0.0

Customize when commuters travel in your city

The Pop Timing API lets you customize the commute time ranges that determine when simulated commuters travel. This is useful for cities with different work patterns or cultural norms.

Demand Window Format#

The day is divided into named demand windows, each described by an object:

typescript
type TimeOfDayRange = {
    key: string;                  // unique identifier β€” used by per-window setters
    name: string;                 // human-readable name (translated)
    start: number;                // start hour, inclusive, 0–23
    end: number;                  // end hour, exclusive, 1–24
    homeDemandMultiplier: number; // weighting for home-to-work trip start times in this window
    workDemandMultiplier: number; // weighting for work-to-home trip start times in this window
};

homeDemandMultiplier raises the probability that a pop's morning departure (leaving home for work) falls inside this window. workDemandMultiplier does the same for the evening departure (leaving work for home). Larger values = more pops depart in this window.

Default Demand Windows#

KeyHoursHome demandWork demand
LateNight0 – 30.150.15
EarlyMorning3 – 60.30.3
EarlyMorningRush6 – 71.00.3
PeakMorningRush7 – 102.50.3
LateMorningRush10 – 111.00.8
Midday11 – 150.80.8
EarlyEveningRush15 – 160.81.0
PeakEveningRush16 – 190.32.5
LateEveningRush19 – 200.31.0
EarlyNight20 – 230.30.3
Night23 – 240.150.15

So by default the morning homeβ†’work peak lands at 7–10am (PeakMorningRush) and the evening workβ†’home peak at 4–7pm (PeakEveningRush).

Get Current Ranges#

javascript
const ranges = window.SubwayBuilderAPI.popTiming.getCommuteTimeRanges();
console.log(ranges[3]);
// { key: 'PeakMorningRush', name: 'Peak Morning Rush',
//   start: 7, end: 10, homeDemandMultiplier: 2.5, workDemandMultiplier: 0.3 }

Read the Current Demand Phase#

Don't hard-code your own hour→demand table to detect rush hour — commute windows are moddable (see below), so a hard-coded table breaks as soon as any mod (including yours) changes them. Read the live values instead:

javascript
const api = window.SubwayBuilderAPI;

// The commute window active right now, including any mod overrides
const range = api.popTiming.getCurrentCommuteTimeRange();
// { key: 'PeakMorningRush', name: 'Peak Morning Rush',
//   start: 7, end: 10, homeDemandMultiplier: 2.5, workDemandMultiplier: 0.3 }

// The demand tier for the current game hour β€” the same
// 'veryLow' | 'low' | 'medium' | 'high' tier train scheduling uses
// to decide which trains run (route.trainSchedule.highDemand etc.)
const tier = api.popTiming.getDemandLevel();      // e.g. 'high'
const atNoon = api.popTiming.getDemandLevel(12);  // tier for a specific hour

// The full hour→tier table (returned as a copy)
const table = api.popTiming.getHourDemandLevels();
// { 0: 'veryLow', 1: 'veryLow', ..., 8: 'high', ..., 23: 'veryLow' }

getDemandLevel returns null for hours outside 0–23.

Tweak One Window#

Use setCommuteTimeRange(key, partial) to change just the hours, just the multipliers, or both. Fields you don't pass keep their current value, and the range's key itself can't be reassigned.

javascript
const api = window.SubwayBuilderAPI;

// Shift peak morning rush an hour later
api.popTiming.setCommuteTimeRange('PeakMorningRush', { start: 8, end: 11 });

// Make the evening commute even spikier without changing its hours
api.popTiming.setCommuteTimeRange('PeakEveningRush', { homeDemandMultiplier: 0.2, workDemandMultiplier: 3 });

// Look up one window
const peak = api.popTiming.getCommuteTimeRange('PeakMorningRush');

// Restore just this one window to its default β€” other overrides stay in place
api.popTiming.resetCommuteTimeRange('PeakMorningRush');

setCommuteTimeRange returns true if the key matched a known window. Invalid values (out-of-range hours, non-numbers, unknown keys) are rejected and logged to the console.

Replace All Windows#

Use setCommuteTimeRanges(ranges) when you want to redefine the full schedule in one shot. Every range needs start, end, homeDemandMultiplier, and workDemandMultiplier.

javascript
api.popTiming.setCommuteTimeRanges([
    { key: 'Off', name: 'Off Peak', start: 0, end: 6,
      homeDemandMultiplier: 0.25, workDemandMultiplier: 0.25 },
    { key: 'Morning', name: 'Morning', start: 6, end: 10,
      homeDemandMultiplier: 2, workDemandMultiplier: 0.5 },
    { key: 'Midday', name: 'Midday', start: 10, end: 16,
      homeDemandMultiplier: 0.75, workDemandMultiplier: 0.75 },
    { key: 'Evening', name: 'Evening', start: 16, end: 20,
      homeDemandMultiplier: 0.5, workDemandMultiplier: 2 },
    { key: 'Night', name: 'Night', start: 20, end: 24,
      homeDemandMultiplier: 0.25, workDemandMultiplier: 0.25 },
]);

// Reset everything back to the built-in schedule
api.popTiming.resetCommuteTimeRanges();

Time Format#

Times are in 24-hour format:

  • 0 = midnight
  • 12 = noon
  • 17 = 5pm
  • 23 = 11pm

Use Cases#

Spanish Schedule (Late Lunch, Late Dinner)#

javascript
api.popTiming.setCommuteTimeRange('PeakMorningRush', { start: 8, end: 11 });
api.popTiming.setCommuteTimeRange('PeakEveningRush', { start: 19, end: 22 });

Industrial City (Three Shifts)#

javascript
api.popTiming.setCommuteTimeRanges([
    { key: 'Shift1Arrive', name: 'Shift 1', start: 5, end: 7,
      homeDemandMultiplier: 2, workDemandMultiplier: 0.25 },
    { key: 'Shift1Leave', name: 'Shift 1 End', start: 13, end: 15,
      homeDemandMultiplier: 0.25, workDemandMultiplier: 2 },
    { key: 'Shift2Arrive', name: 'Shift 2', start: 13, end: 15,
      homeDemandMultiplier: 1.5, workDemandMultiplier: 0.25 },
    { key: 'Shift2Leave', name: 'Shift 2 End', start: 21, end: 23,
      homeDemandMultiplier: 0.25, workDemandMultiplier: 1.5 },
    // ...etc
]);

Effect on Gameplay#

Changing commute times affects:

  • Ridership patterns: Passengers appear during your defined rush hours
  • Revenue distribution: Ticket sales spike during rush periods
  • Capacity planning: Trains need to handle peak loads at different times
  • Route optimization: Players may need to adjust service for unusual schedules
Changes take effect the next time pops are assigned commute times (typically when demand data loads for a city). Pops already in the simulation keep their existing departure times until demand is regenerated.

Commute Curve Dampening#

Most pops follow the rush-hour curve above. Two special pop types β€” university students (jobs starting with UNI_) and airport workers (jobs starting with AIR_) β€” get a flatter distribution because their schedules are more spread out across the day.

The flatness is controlled by a dampening factor:

  • 0 = use the raw rush-hour curve, no smoothing (same as regular workers)
  • 1 = fully flat: every hour of the day is equally likely
  • Values in between linearly interpolate each hour toward the day's mean

Defaults:

Pop typeDefault dampening
University students (UNI_)0.3
Airport workers (AIR_)0.5 (also mirrored so home/work flows are equal)

Get current dampening#

javascript
const api = window.SubwayBuilderAPI;
console.log(api.popTiming.getStudentDampening()); // 0.3
console.log(api.popTiming.getAirportDampening()); // 0.5

Override dampening#

javascript
// Make students commute almost uniformly throughout the day
api.popTiming.setStudentDampening(0.8);

// Make airport workers truly 24/7 β€” equal at every hour
api.popTiming.setAirportDampening(1);

// Make students follow the regular rush-hour curve (no smoothing)
api.popTiming.setStudentDampening(0);

Passing null (or calling resetCommuteDampening()) restores the defaults:

javascript
api.popTiming.setStudentDampening(null);   // reset just students
api.popTiming.setAirportDampening(null);   // reset just airports
api.popTiming.resetCommuteDampening();     // reset both at once

Values must be in the [0, 1] range. Anything else is rejected and logged to the console.

**When does this take effect?** Dampening is read each time pops are assigned new commute times (typically when demand data loads for a city). Already-assigned pops keep their existing departure times until demand is regenerated.

Complete Example#

javascript
const api = window.SubwayBuilderAPI;

// Register a custom city with matching commute times
api.registerCity({
    code: 'TOKYO',
    name: 'Tokyo'
    // ... other city config
});

// Tokyo has extremely early morning rush
api.popTiming.setCommuteTimeRanges([
    { start: 6, end: 10 }, // Long morning rush
    { start: 17, end: 21 } // Long evening rush
]);

// The simulation will now have passengers
// commuting during these hours