Introduction
As the year drew towards Christmas, we wanted to build something fun to celebrate another great year at Tekantis, and also to show the creative possibilities of Icon Map in Power BI. This led us to the idea of animating Santa’s journey around the world inside Power BI.
Getting the Data
The first step was finding reliable data for Santa’s route on Christmas Eve. Fortunately, NORAD’s long-running Santa tracking project publishes openly accessible information, which gave us exactly what we needed. Their data has been made available in a convenient JSON format, ideal for loading directly into Power BI.
We used the official NORAD Santa Tracker site as the authoritative source. NORAD has been tracking Santa since 1955, after a misprinted phone number in a newspaper led children to call the Continental Air Defence Command asking for Santa’s whereabouts. Rather than turn them away, the officers on duty played along, and a Christmas tradition was born. Today, NORAD uses radar systems, satellites, high‑altitude sensors and even a network of volunteer spotters to "track" Santa’s sleigh across the globe in real time.
The openly available JSON version of the route, provided the precise structure needed for our Power Query transformations.
With the route in hand, our goal was to bring it to life in Power BI and Icon Map Slicer, using a combination of geospatial techniques, animation logic and a healthy dose of Christmas spirit.
Loading and Shaping the Data in Power Query
The Santa route is stored as a JSON list of stops, each with key attributes including location, timing, region, population and even a selection of images. The first task was to convert this into a clean tabular structure suitable for Power BI.
Using Power Query, we expanded the JSON, extracted latitude and longitude, selected the first available photo for each stop and converted the timestamps from epoch milliseconds into human-readable form. To make the route visualisation easier, we also created lat_to and long_to columns, pointing to the next stop in the sequence. This allowed us to draw 3D route lines step by step.
Below is the final Power Query script used:
let
// 1. Load JSON file
Source =
Json.Document(
File.Contents("C:\Users\brynn\Downloads\route_santa_en.json")
),
// 2. Extract destinations list
Destinations = Source[destinations],
// 3. Convert list to table
#"Destinations Table" =
Table.FromList(
Destinations,
Splitter.SplitByNothing(),
null,
null,
ExtraValues.Error
),
// 4. Expand destination records
#"Expanded Destination" =
Table.ExpandRecordColumn(
#"Destinations Table",
"Column1",
{"id", "arrival", "departure", "population", "presentsDelivered", "city", "region", "location", "details"},
{"id", "arrival", "departure", "population", "presentsDelivered", "city", "region", "location", "details"}
),
// 5. Expand location → lat / lng
#"Expanded Location" =
Table.ExpandRecordColumn(
#"Expanded Destination",
"location",
{"lat", "lng"},
{"Latitude", "Longitude"}
),
// 6. Expand details to get timezone and photos list
#"Expanded Details" =
Table.ExpandRecordColumn(
#"Expanded Location",
"details",
{"timezone", "photos"},
{"timezone", "photos"}
),
// 7. Take only the FIRST photo in the photos list for each stop
#"Added First Photo" =
Table.AddColumn(
#"Expanded Details",
"FirstPhoto",
each
let
p = [photos]
in
if p = null or List.IsEmpty(p) then null else p{0},
type any
),
// 8. Remove the original photos list
#"Removed Photos Column" =
Table.RemoveColumns(
#"Added First Photo",
{"photos"}
),
// 9. Expand FirstPhoto record into separate columns
#"Expanded First Photo" =
Table.ExpandRecordColumn(
#"Removed Photos Column",
"FirstPhoto",
{"url", "attributionHtml", "lg"},
{"photo_url", "photo_attribution", "photo_lg"}
),
// 10. Add Index to maintain route order
#"Added Index" =
Table.AddIndexColumn(
#"Expanded First Photo",
"Index",
0,
1,
Int64.Type
),
// 11. Add lat_to column for next city
#"Added lat_to" =
Table.AddColumn(
#"Added Index",
"lat_to",
each
let
i = [Index]
in
try #"Added Index"{i + 1}[Latitude] otherwise null,
type number
),
// 12. Add long_to column for next city
#"Added long_to" =
Table.AddColumn(
#"Added lat_to",
"long_to",
each
let
i = [Index]
in
try #"Added Index"{i + 1}[Longitude] otherwise null,
type number
),
// 13. Add arrival_utc (datetimezone, UTC)
#"Added arrival_utc" =
Table.AddColumn(
#"Added long_to",
"arrival_utc",
each
let
epoch = #datetimezone(1970, 1, 1, 0, 0, 0, 0, 0),
secs = [arrival] / 1000
in
epoch + #duration(0, 0, 0, secs),
type datetimezone
),
// 14. Add departure_utc (datetimezone, UTC)
#"Added departure_utc" =
Table.AddColumn(
#"Added arrival_utc",
"departure_utc",
each
let
epoch = #datetimezone(1970, 1, 1, 0, 0, 0, 0, 0),
secs = [departure] / 1000
in
epoch + #duration(0, 0, 0, secs),
type datetimezone
),
// 15. Add arrival_local (datetime, using timezone offset in seconds)
#"Added arrival_local" =
Table.AddColumn(
#"Added departure_utc",
"arrival_local",
each
let
utc = [arrival_utc],
offsetHours = Number.From([timezone]) / 3600.0,
zoned = DateTimeZone.SwitchZone(utc, offsetHours)
in
DateTimeZone.RemoveZone(zoned),
type datetime
),
// 16. Add departure_local (datetime, using timezone offset in seconds)
#"Added departure_local" =
Table.AddColumn(
#"Added arrival_local",
"departure_local",
each
let
utc = [departure_utc],
offsetHours = Number.From([timezone]) / 3600.0,
zoned = DateTimeZone.SwitchZone(utc, offsetHours)
in
DateTimeZone.RemoveZone(zoned),
type datetime
),
// 17. Reorder columns neatly
#"Reordered Columns" =
Table.ReorderColumns(
#"Added departure_local",
{
"id",
"city",
"region",
"population",
"presentsDelivered",
"Latitude",
"Longitude",
"lat_to",
"long_to",
"arrival",
"departure",
"timezone",
"arrival_utc",
"departure_utc",
"arrival_local",
"departure_local",
"photo_url",
"photo_attribution",
"photo_lg",
"Index"
}
)
in
#"Reordered Columns"
Animating the Journey with Icon Map Pro and Play Axis
With the data prepared, the next step was to animate Santa’s progress along the route. Icon Map Slicer provided the geospatial foundation, enabling us to plot the 3D route lines simply using the latitude & longitude coordinates in our data. To bring the journey to life, we used the Play Axis custom visual, enabling us to advance through each stop one at a time.
However, we wanted a slightly more sophisticated effect than simply showing the current step. Specifically, we wanted all previous steps to remain visible, so that Santa’s path gradually drew itself across the globe.
Allowing Route History to Persist
The Play Axis visual works by filtering the dataset to a single selected value, which ordinarily causes all other steps to disappear. To maintain history, we added a disconnected Step table and a set of DAX measures ensuring that instead of filtering strictly to the selected index, the report would treat all rows with an index less than or equal to the current step as visible.
Step Table
Step =
SELECTCOLUMNS (
DISTINCT ( 'Route'[Index] ),
"StepIndex", 'Route'[Index]
)
Current Step Measure
Current Step =
VAR Selected = SELECTEDVALUE ( Step[StepIndex] )
RETURN IF ( NOT ISBLANK ( Selected ), Selected )
Show-Up-To-Step Logic
Show Up To Step =
VAR ThisIndex = MAX ( 'Route'[Index] )
VAR StepNow = [Current Step]
RETURN IF ( NOT ISBLANK ( StepNow ) && ThisIndex <= StepNow, 1, 0 )
This is actually quite useful reusable pattern. While we used it to animate Santa’s global journey, the same technique could be applied to real-world logistics, vehicle telematics, sensor playback, or any scenario where you want an animation that preserves its historical trail.
Surfacing Images, Towns and Presents Delivered
To enhance the storytelling, we added further measures that responded to the current step. These allowed us to show:
- the latest photo for each location,
- the name of the town Santa had reached,
- how many presents he had delivered cumulatively.
By connecting these measures into card visuals, the report updated dynamically, giving a lively running commentary as the animation progressed.
Putting It All Together
Once assembled, the result was a cheerful, fully animated Santa tracker built entirely inside Power BI. To round it off, we used ChatGPT to generate a prompt for a Christmas-themed song inspired by the idea, which we then loaded into Suno.ai to create a very catchy song, it is remarkable to see how far generative AI has come since experimenting with the early OpenAI music generator back in 2024.
We've also made a quick custom visual to play the song within the Power BI report!