Skip to main content

Weather Preview

Overview

In this tutorial, we will use Motion Canvas to integrate real-time data from a weather API into our animations. Building on your knowledge of REST API fundamentals, we’ll focus on fetching weather data, processing it for specific attributes like temperature and conditions, and dynamically visualizing this data. This hands-on approach will demonstrate how to transform raw JSON responses into compelling animations that adapt to real-world weather changes. Let’s dive in and bring your animations to life with live weather data!

Output Video

Tutorial Video

Prerequisites

  • Node.js installed
    • Version v20.17.0 was used during this tutorial
    • A guide on Node.js is coming in the future
  • Your favorite IDE
    • VS Code was used in this tutorial
  • Completion of the Motion Canvas Rest API project

Tutorial

  1. Create a new Motion Canvas Project using the following command and inputs when prompted
    1. npm init @motion-canvas@latest
      1. Project name: weather
      2. Project path: weather
      3. Languages: TypeScript (Recommended)
      4. How would you like to render your animation? Image Sequence & Video (FFmpeg)
  2. Now let's move into the new directory, install the dependencies, and start the web viewer
    1. cd weather
    2. npm install
    3. npm start
      1. In the video, the command code . is a shortcut from VS Code.
  3. Rename the project
    1. In ./src/project.ts rename example to be weather
      import {makeProject} from '@motion-canvas/core';

      import weather from './scenes/weather?scene';

      export default makeProject({
      scenes: [weather],
      });
    2. Rename the file ./src/scenes/example.tsx to ./src/scenes/weather.tsx
  4. Change the background
    1. In the UI Viewer, under Video Settings, change the background to be white (drag the color selector to the top left conner) and the alpha layer to 1
    2. The UI Viewer should show rgb(255,255,255)
    background
Note

The rest of this tutorial will happen in our ./src/scenes/weather.tsx file.

  1. Create a Code Block

    1. Update the 1st line to be import {Code, makeScene2D} from '@motion-canvas/2d'; as this will remove the default settings and let us use the Code Block in our animation.

    2. Remove the 2nd line for @motion-canvas/core as we will not need to make a reference at this time.

    3. Update the body of makeScene2D to just contain a template for our code.

      view.add(<Code 
      fontSize={15}
      code={"Hello World"}
      fill={"black"}
      />);
      1. This will let us make sure everything is working first.
      Note

      It is a good practice to test your code often and in small steps while writing.

  2. Getting Weather Data

    1. Now it is time to get weather data from online. The way we do this is by a REST API. One good source for weather data is Open-Meteo which can be used for free for our purpose.
    2. With this site, there is a selector to help make the API that we will use.
      1. Select the Latitude and Longitude that you want including the Timezone.
      background
      1. Unselect everything in the Hourly section.
      background
      1. In the Daily section, select the following:
        1. Weather code
        2. Maximum Temperature (2 m)
        3. Minimum Temperature (2 m)
      background
      1. These 3 options will give us the temperature for the day as well as the weather status.
        1. For Weather code you can map the code to the real value by scrolling to the bottom of the Open-Meteo page.
        background
      2. In the Settings section, I am going to change the Units to be what I use being from the USA. If you use a different set of units, please select what you normally use.
      background
      1. If you want, you can click Reload Chart and Open-Meteo will show a chart of the data.
      background background
      1. To view the actual data in the API, below the chart there is an Open in new tab link which will open the API in a new tab or you can just copy the URL in the box below.
    3. With the URL copied, add the logic for calling an API into Motion Canvas.
      1. You can use the module that you want but this tutorial will use fetch
      2. The Motion Canvas Rest API project tutorial walks you through this.
      3. The code to add will look the following:
      const response = yield fetch('https://api.open-meteo.com/v1/forecast?latitude=41.85&longitude=-87.65&daily=weather_code,temperature_2m_max,temperature_2m_min&temperature_unit=fahrenheit&wind_speed_unit=mph&precipitation_unit=inch&timeformat=unixtime&timezone=America%2FChicago')
      const data = yield response.json()
  3. Update the Code Block

    1. Update the code section of the Code Block to be JSON.stringify(data, null, 2) instead of "Hello World".
  4. Parsing API Data

    1. If you looked at the JSON response of the API, you will see that it can be not as easy to read as the daily section just has an array for each item we want. To make it easier to understand and use, we are going to make our own object with this information that we will then use. If you want to skip this step, you will have to figure how how to ingest this data yourself.
    background
    1. The first part is we want to add is our new object for storing information. Remember, for this tutorial we are using typescript so we must define our types for this object.
      1. From the response body, we want to collect the date, temperature_2m_max, temperature_2m_min, and weather_code. The new object will be an array of objects; 1 object per day.
      const temps: { date: Date; day: string; high: string; low: string; code: string; }[] = []
      1. Place the above line between const data = yield response.json() and our code block.
      2. Under the line we just added, we can also create a mapping for each day of the week since NodeJS dates start with 0 for Sunday and don't give the actually names. The names in the array are abbreviated to save space in the animation.
      const weekday = ["Sun","Mon","Tues","Wed","Thur","Fri","Sat"]
      1. Below our mapping we can finally parse the API Response. We can do this by looping through each of the time elements in the API Response and placing the respective data in our new array. As part of this parsing, I am creating a new date object to help with debugging and making sure everything is correct. I am also using our mapping to get what day it is, rounding the max and min values as we want whole numbers, not decimals, and converting the weather code to a string for logic later on.
      for(let i=0; i<data.daily.time.length; i++){
      temps.push({
      date: new Date(data.daily.time[i] * 1000),
      day: weekday[new Date(data.daily.time[i] * 1000).getDay()],
      high: Math.round(data.daily.temperature_2m_max[i]).toString(),
      low: Math.round(data.daily.temperature_2m_min[i]).toString(),
      code: data.daily.weather_code[i].toString()
      })
      }
      1. Update our code block to display temps instead of data.
      view.add(<Code 
      fontSize={15}
      code={JSON.stringify(temps, null, 2)}
      fill={"black"}
      />);
  5. Adding a Header

    1. For the weather preview, a lot of examples have a header or bar at the top with some text. This is used to say where the weather is from or about and also more info that might be useful.
    2. For this tutorial, we are just going to have a header that says 7-DAY FORECAST in the upper left of the screen.
    3. From @motion-canvas/2d we will be using Node, Spline and the Txt components. Please import them at the correct place at the top of the file.
    import {Code, makeScene2D, Txt, Node, Spline} from '@motion-canvas/2d';
    1. Under the Code block we are going to add our header to the view. To make the shape of the Spline you just have to play around with the shape with it positioned in the center then when it is the shape that you want, change the position to be where you want. I find that it is harder to put the Spline where you want first because if you need to move it, it might be more of a challenge.
    view.add(
    <Node>
    <Spline
    lineWidth={4}
    fill={'#007FFF'}
    smoothness={0}
    position={[-800, -420]}
    points={[
    [250, 0],
    [350, -100],
    [-200, -100],
    [-200, 0]
    ]}
    />
    <Txt
    text={"7-DAY FORECAST"}
    x={-750}
    y={-475}
    />
    </Node>
    )
  6. Creating a template for each day

    1. Next we are going to create a template for each of the 7 days that we are going to display.
    2. To make our code more efficient, we are going to loop through an array to create the weather preview.
    3. From @motion-canvas/2d we will be adding the Rect component. Please import it at the correct place at the top of the file.
    import {Code, makeScene2D, Txt, Node, Spline, Rect} from '@motion-canvas/2d';
    1. From @motion-canvas/core we will be adding the range component. Please import it at the correct place at the top of the file.
    import {range} from '@motion-canvas/core';
    1. Underneath the logic for our header, create 7 Rect objects with each inside of their own Node. The entire loop should also be in its own Node.
    view.add(
    <Node>
    {
    range(7).map(i => (
    <Node>
    <Rect
    width={225}
    height={1080/2}
    x={-810 + 270 * i}
    fill="#007FFF"
    radius={10}
    />
    </Node>
    ))
    }
    </Node>
    )
  7. Adding a gradient

    1. We now have 7 evenly spaced Rect objects but we can make it look slightly better by adding a gradient. A gradient is how we would show one color transitioning into another.
    2. From @motion-canvas/2d we will be adding the Gradient component. Please import it at the correct place at the top of the file.
    import {Code, makeScene2D, Txt, Node, Spline, Rect, Gradient} from '@motion-canvas/2d';
    1. When creating a gradient in motion canvas, we need to define a new Gradient object that goes from one color to another. We also have to define from what direction and how far the gradient will go and this can be defined with the fields fromY, toY, fromX, and toX. If you want to read more about Gradient, you can read the official documentation.
    warning

    Unfortunately, there is not good documentation on the Motion Canvas site to learn how to make a Gradient. Fortunately for you, I was able to figure it out. 😀

    The best way to figure out how something works is by using the different fields in the documentation and seeing what they do.

    1. Putting it all together, creating a new Gradient object would look like:
    const gradient = new Gradient({
    fromY: -160,
    toY: 160,
    stops: [
    {offset: 0, color: '#7CB9E8'},
    {offset: 1, color: '#007FFF'},
    ],
    });
    1. Place this logic inside the makeScene2D function.
    Placement

    I tend to place it close to the code I am working on but it could also be placed at the top of the function if you wanted it more organized.

    Technically, you could also place it outside of the makeScene2D function between this function and the import it at the top of the file.

    1. To use this gradient, update our Rect that we just added (From Step 11 Part 5) to have the fill be the gradient instead.

    Old Version

    view.add(
    <Node>
    {
    range(7).map(i => (
    <Node>
    <Rect
    width={225}
    height={1080/2}
    x={-810 + 270 * i}
    fill="#007FFF"
    radius={10}
    />
    </Node>
    ))
    }
    </Node>
    )

    New Version

    view.add(
    <Node>
    {
    range(7).map(i => (
    <Node>
    <Rect
    width={225}
    height={1080/2}
    x={-810 + 270 * i}
    fill={gradient}
    radius={10}
    />
    </Node>
    ))
    }
    </Node>
    )
  8. Adding the day name

    1. To add some more style, we can add a different Rect on top of the first one, with the new gradient, for the name of the day. For the color, a darker color seems to be a nice contrast. with the gradient.
    Reminder

    Remember to have the new Rect after the first one so that it sits on top of the first one.

    <Rect 
    width={225}
    height={1080/10}
    x={-810 + 270 * i}
    y={-220}
    fill="#007FFF"
    radius={10}
    />
    1. To add what day it is, we can add a new Txt component over our Rect we just created.
    Looping

    Because we are inside of a loop, we can easily get the correct information for each day without additional logic.

    <Txt
    text={temps[i].day}
    x={-810 + 270 * i}
    y={-220}
    />
  9. Adding the High and Low

    1. To add the high and low temperatures, can do the same thing as the last step (Step 14 part 2). For extra styling, I made the high to be a larger fontSize and added some color to each of the values. By placing them lower on the main Rect with the gradient, we can create something that is starting to look more like a weather preview.
    <Txt
    fontSize={70}
    text={temps[i].high}
    x={-810 + 270 * i}
    y={90}
    fill={"white"}
    />
    <Txt
    text={temps[i].low}
    x={-810 + 270 * i}
    y={150}
    fill={"#7CB9EF"}
    />
  10. Adding the weather type

    1. Lastly we want to add what kind of weather we are going to have on a given day.
    2. Currently we have number for the weather code, let's create a mapping function to convert this code into a descriptive text.
    3. At the top of the function or file (below the imports), create the following mapping function using the data from the bottom of the Open-Meteo page. For our mapping function, a switch statement can be used as an easy way to map one value to another. An IF-ELSE statement could also be used but I find a switch to be better when comparing the same variable across several values.
    Weather Mapping

    In this tutorial, I did not create a full mapping and only the weather codes that I received. It is recommended that you do the full mapping however as the weather changes all the time and you might get weather you have mapped in the future.

    For this map, I also did the shortened version of the weather text because the full version would not display how I wanted it.

    Because of this, I am keeping to the groups of weather patterns and the not variations of them.

    const WmoToWord = (code: string) => {
    switch (code){
    case "0":
    return "Clear Sky"
    case "3":
    return "Overcast"
    case "45":
    return "Fog"
    case "55":
    return "Drizzle"
    case "61":
    return "Rain"
    default:
    return code
    }
    }
    1. Now using this mapping, we can add another Txt to our weather preview below the high and low data. To make a more stylistic look, a grey for this seems better.
    <Txt
    text={WmoToWord(temps[i].code)}
    x={-810 + 270 * i}
    y={215}
    fill={"#DDDDDD"}
    />
  11. Adding an image for the weather

    1. With the same pattern as the weather description, we can create a new mapping with the images of the weather. To make is is easy, weather emojis can be used.
    Images

    If you wanted, you could also use real images or try to create different animations for each of the weather patterns and use them instead of emojis.

    1. Place the mapping next two the other weather mapping.
      const WmoToImage = (code: string) => {
    switch (code){
    case "0":
    return "☀️"
    case "3":
    return "🌤️"
    case "45":
    return "☁️"
    case "55":
    return "☔️"
    case "61":
    return "🌧️"
    default:
    return code
    }
    }
    1. Now using this mapping, we can add another Txt to our weather preview between the day text and the high data. To make a more stylistic look, a larger font for this seems better.
    <Txt
    fontSize={150}
    text={WmoToImage(temps[i].code)}
    x={-810 + 270 * i}
    y={-50}
    />
Code Block

At this time, we no longer need the code block that we had in the background while making the weather preview.

We can now delete the logic for our code block and remove the Code import from @motion-canvas/2d.

  1. Adding a background

    1. Now the weather preview is completed, however we can make it better with so additional background and music.
    2. For the background place an image that you have in the public folder. The image in this tutorial is called winter.jpg
    Copyright

    The image in this tutorial is not owned by me as it was a random image found online. Because I do not own the Copyright for this image, I can not share it in this tutorial.

    1. At the top of the page, add Img to the imports for @motion-canvas/2d
    import {makeScene2D, Txt, Node, Spline, Rect, Gradient, Img} from '@motion-canvas/2d';
    1. Below the existing imports, import the image:
    import winter from '../../public/winter.jpg?url';
    1. Above all of the other view.add we can add our image to the scene:
    view.add(
    <Img
    scale={3.5}
    src={winter}
    />
    );
    Image Scale

    My image was smaller then the resolution of the scene (1920 x 1080) so I needed to scale the image until it took over the entire scene.

  2. Adding some music

    1. To make this animation even better let's add some music to make it sound more like the Weather Channel.
    Copyright

    The audio in this tutorial is not owned by me as it was a random audio clip found online. Because I do not own the Copyright for this audio, I can not share it in this tutorial.

    For protection from youtube copyright, the audio played will only be 5 seconds long and I am talking over it in the tutorial video.

    1. Place our music that we have in our public folder along side our background image. The audio in this tutorial is called music.mp3.
    2. In our ./src/project.ts file, add a new audio import under weather and also add the audio to our project.
    3. The entire file now looks like the following:
    import {makeProject} from '@motion-canvas/core';

    import weather from './scenes/weather?scene';
    import audio from '../public/music.mp3?url'

    export default makeProject({
    scenes: [weather],
    audio: audio
    });
    Audio Clips

    The current version of Motion Canvas only supports 1 audio track and the animation rendering ends when either the audio is done or the animations complete, whichever one comes first.

    Because of this, we need to add a waitFor at the end of the makeScene2D function to only play the audio since there are no other animations.

    In a future release, Motion Canvas will support multiple audio clips which you can learn more about by reading this pull request: feat: programmable sounds #1082.

    1. As mentioned in the tip above, we need to add a waitFor to our logic.
    2. At the top of the page, add waitFor to the imports for @motion-canvas/core
    import {range, waitFor} from '@motion-canvas/core';
    1. At the bottom of the makeScene2D function add our waitFor for 5 seconds. Remember you can have the seconds be for how long you want it to be but no more then the length of the audio track.
    yield* waitFor(5)
  3. End Results

    import {makeScene2D, Txt, Node, Spline, Rect, Gradient, Img} from '@motion-canvas/2d';
    import {range, waitFor} from '@motion-canvas/core';
    import winter from '../../public/winter.jpg?url';

    export default makeScene2D(function* (view) {
    const response = yield fetch('https://api.open-meteo.com/v1/forecast?latitude=41.85&longitude=-87.65&daily=weather_code,temperature_2m_max,temperature_2m_min&temperature_unit=fahrenheit&wind_speed_unit=mph&precipitation_unit=inch&timeformat=unixtime&timezone=America%2FChicago')
    const data = yield response.json()

    const temps: { date: Date; day: string; high: any; low: any; code: any; }[] = []

    const weekday = ["Sun","Mon","Tues","Wed","Thur","Fri","Sat"]

    const WmoToWord = (code: string) => {
    switch (code){
    case "0":
    return "Clear Sky"
    case "3":
    return "Overcast"
    case "45":
    return "Fog"
    case "55":
    return "Drizzle"
    case "61":
    return "Rain"
    default:
    return code
    }
    }

    const WmoToImage = (code: string) => {
    switch (code){
    case "0":
    return "☀️"
    case "3":
    return "🌤️"
    case "45":
    return "☁️"
    case "55":
    return "☔️"
    case "61":
    return "🌧️"
    default:
    return code
    }
    }

    for(let i=0; i<data.daily.time.length; i++){
    temps.push({
    date: new Date(data.daily.time[i]),
    day: weekday[new Date(data.daily.time[i]).getDay()],
    high: Math.round(data.daily.temperature_2m_max[i]).toString(),
    low: Math.round(data.daily.temperature_2m_min[i]).toString(),
    code: data.daily.weather_code[i].toString()
    })
    }

    view.add(<Img
    scale={3.5}
    src={winter}
    />
    );

    // Heading
    view.add(
    <Node>
    <Spline
    lineWidth={4}
    fill={'#007FFF'}
    smoothness={0}
    position={[-800, -420]}
    points={[
    [250, 0],
    [350, -100],
    [-200, -100],
    [-200, 0]
    ]}
    />
    <Txt
    text={"7-DAY FORECAST"}
    x={-750}
    y={-475}
    />
    </Node>
    )

    // Base Template
    const gradient = new Gradient({
    fromY: -160,
    toY: 160,
    stops: [
    {offset: 0, color: '#7CB9E8'},
    {offset: 1, color: '#007FFF'},
    ],
    });

    view.add(
    <Node>
    {
    range(7).map(i => (
    <Node>
    <Rect
    width={225}
    height={1080/2}
    x={-810 + 270 * i}
    fill={gradient}
    radius={10}
    />
    <Rect
    width={225}
    height={1080/10}
    x={-810 + 270 * i}
    y={-220}
    fill="#007FFF"
    radius={10}
    />
    <Txt
    text={temps[i].day}
    x={-810 + 270 * i}
    y={-220}
    />
    <Txt
    fontSize={70}
    text={temps[i].high}
    x={-810 + 270 * i}
    y={90}
    fill={"white"}
    />
    <Txt
    text={temps[i].low}
    x={-810 + 270 * i}
    y={150}
    fill={"#7CB9EF"}
    />
    <Txt
    text={WmoToWord(temps[i].code)}
    x={-810 + 270 * i}
    y={215}
    fill={"#DDDDDD"}
    />
    <Txt
    fontSize={150}
    text={WmoToImage(temps[i].code)}
    x={-810 + 270 * i}
    y={-50}
    />
    </Node>
    ))
    }
    </Node>
    )

    yield* waitFor(5)
    });
  4. Bonus: Leaning about the API options

    1. If we look again at the API call it is: https://api.open-meteo.com/v1/forecast?latitude=41.85&longitude=-87.65&daily=weather_code,temperature_2m_max,temperature_2m_min&temperature_unit=fahrenheit&wind_speed_unit=mph&precipitation_unit=inch&timeformat=unixtime&timezone=America%2FChicago
    2. Formatting it a little to break it down gets us:
    https://api.open-meteo.com/v1/forecast?
    latitude=41.85 &
    longitude=-87.65 &
    daily=weather_code,temperature_2m_max,temperature_2m_min &
    temperature_unit=fahrenheit &
    wind_speed_unit=mph &
    precipitation_unit=inch &
    timeformat=unixtime &
    timezone=America%2FChicago
    1. We can see that the options we selected in the UI at the beginning are presented here. With this information, we can automate switching between different areas just by dynamically updating the latitude and longitude and calling the API again.
    2. Review the rest of the Open-Meteo page for how the API is used.
    3. With this information, you should be able to put what we created together in one or more functions and cycle through different cities.
Congratulations!!

You have now created a working Rest API call with Motion Canvas. In the next project, we will expand upon these concepts.