Creating a Flow Map with JavaScript: A Step-by-Step Guide

Flow maps are a powerful way to represent the movement of objects between different geographic locations and are very easy to implement with JavaScript. In fact, they combine the functionality of a map and a flowchart. This type of visualization shows the direction of movement of people, goods, money or information, as well as their number.

In this tutorial, we will walk you through the process of creating a flow map. And to make this process more practical, we use the example of visualizing the number of students from India studying in different countries. By following this tutorial, you will learn how to create custom flow maps with JS for any kind of data.

map preview

Below you can see what our final flow map will look like.

Creating a basic flow map with JavaScript

Using JS, you can build a flow map in four main steps:

  1. Create a web page using HTML.
  2. Include required JS files.
  3. Add data.
  4. Write JS code to draw the chart.

Having a good understanding of HTML, CSS, and JS will come in handy, but don’t worry if these tools are new to you. In this guide, I will describe each step in detail, making the task as easy as possible for you.

▍ 1. Create a web page in HTML

First, let’s create a simple HTML page as a container to display our map. We do this by creating an element

<div>

and setting its attribute

id

on

container

. This will allow us to refer to the created container later when we add the map.

In order for the flow map to be displayed on the entire page, we will use CSS to set the height and width <div> by 100%. You can customize the styling to your liking.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Flow Map</title>
    <style type="text/css">      
      html, body, #container {
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
      }
    </style>
  </head>
  <body>
    <div id="container"></div>
  </body>
</html>

▍ 2. Adding required JavaScript files

Next, we will add the necessary scripts to build the flow map to the header section of our HTML file.

The easiest and fastest way to create an interactive flow map is to use JS graphics library. However, not all libraries support this feature. For this guide, we will be using JS Chartswhich, in addition to supporting flow maps, also has an excellent mapping documentation and many ready-made examples.

To create a map, we need to add some modules AnyChart: Core and Geo Map modules for data visualization, as well as geodata for the map. To process geographic coordinates, we use the library Proj4js, so it’s included too. In addition, to add scaling capabilities to the UI, we need the AnyChart Common UI module and CSS.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Flow Map</title>
    <script src="https://cdn.anychart.com/releases/8.11.0/js/anychart-core.min.js"></script>
    <script src="https://cdn.anychart.com/releases/8.11.0/js/anychart-map.min.js"></script>
    <script src="https://cdn.anychart.com/releases/8.11.0/geodata/custom/world/world.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.8.0/proj4.js"></script>
    <script src="https://cdn.anychart.com/releases/8.11.0/js/anychart-ui.min.js"></script>
    <link rel="stylesheet" href="https://cdn.anychart.com/releases/8.11.0/css/anychart-ui.min.css">
    <link rel="stylesheet" href="https://cdn.anychart.com/releases/8.11.0/fonts/css/anychart-font.min.css">
    <style type="text/css">      
      html, body, #container {
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
      }
    </style>
  </head>
  <body>
    <div id="container"></div>
    <script>
      // Здесь будет находиться JS-код карты потоков.
    </script>
  </body>
</html>

▍ 3. Adding data

In this guide, we will be visualizing the top 10 countries to which Indian students were sent in 2022. The data source here is the Indian Ministry of Foreign Affairs, which provides information on

the number of students studying abroad

.

To select the top 10 countries, I analyzed the data and selected the following based on student enrollment: Australia, Canada, Germany, Oman, Qatar, Russian Federation, Saudi Arabia, United Arab Emirates, United Kingdom and United States.

When creating the flow map, we will use the capitals of these countries as the endpoints for the students, and New Delhi, India as the starting point. I determined the latitude and longitude coordinates of the capitals using the site LatLong.net. The table below shows the list of selected countries, as well as the total number of students from India studying in them and the coordinates of the capitals.

The following is the dataset that we will use to create the flow map. He is

array of objects

, each of which reflects one direction of flow. For all sites, the latitude and longitude of the starting and ending points are indicated, as well as the name of the target country and the number of students studying in it.

var data = [
  { points: [28.610198, 77.207584, -30.59, 145.94], to: "Australia", total: 100009 },
  { points: [28.610198, 77.207584, 45.41, -75.69], to: "Canada", total: 183310 },
  { points: [28.610198, 77.207584, 52.51, 13.40], to: "Germany", total: 34864 },
  { points: [28.610198, 77.207584, 23.58, 58.38], to: "Oman", total: 39550 },
  { points: [28.610198, 77.207584, 25.28, 51.53], to: "Qatar", total: 46000 },
  { points: [28.610198, 77.207584, 55.74, 37.62], to: "Russian Federation", total: 18039 },
  { points: [28.610198, 77.207584, 24.71, 46.67], to: "Saudi Arabia", total: 65800 },
  { points: [28.610198, 77.207584, 24.45, 54.37], to: "United Arab Emirates", total: 164000 },
  { points: [28.610198, 77.207584, 52.66, -0.95], to: "United Kingdom", total: 55465 },
  { points: [28.610198, 77.207584, 38.88, -77.03], to: "United States", total: 465791 }
];

▍ 4. Writing JS-code to draw the map

Now that the data is ready, it’s time to move on to the main part – placing the flow map on the web page.

Before we start, we need to make sure that our code is executed only once, and the page loads fully. We will do this using the function anychart.onDocumentReady()which will include all the thread map code.

<script>
  anychart.onDocumentReady(function () {
    // Здесь будет находиться код карты потоков.
  });
</script>

Next, we will add the data that we prepared in the first step.

anychart.onDocumentReady(function () {
  var data = [
    { points: [28.610198, 77.207584, -30.59, 145.94], to: "Australia", total:    100009 },
    { points: [28.610198, 77.207584, 45.41, -75.69], to: "Canada", total: 183310 },
    { points: [28.610198, 77.207584, 52.51, 13.40], to: "Germany", total: 34864 },
    { points: [28.610198, 77.207584, 23.58, 58.38], to: "Oman", total: 39550 },
    { points: [28.610198, 77.207584, 25.28, 51.53], to: "Qatar", total: 46000 },
    { points: [28.610198, 77.207584, 55.74, 37.62], to: "Russian Federation", total: 18039 },
    { points: [28.610198, 77.207584, 24.71, 46.67], to: "Saudi Arabia", total: 65800 },
    { points: [28.610198, 77.207584, 24.45, 54.37], to: "United Arab Emirates", total: 164000 },
    { points: [28.610198, 77.207584, 52.66, -0.95], to: "United Kingdom", total: 55465 },
    { points: [28.610198, 77.207584, 38.88, -77.03], to: "United States", total: 465791 }
  ];
});

To create a flow map chart, we use the AnyChart function

connector()

passing it the data for the trunks and including the geodata of the world map.

var map = anychart.connector();
var connectorSeries = map.connector(data);
map.geoData(anychart.maps.world);

Next, we’ll set labels and add arrows to the end of each stream’s line.

connectorSeries
  .labels()
  .enabled(true)
  .position('100%')
  .format(function () {
    return this.getData('to')
  });

connectorSeries
  .markers()
  .position('100%')
  .size(12);

Now we need to give our flow map a title.

map.title("Top 10 Destination Countries for Indian Students Studying Abroad");

Finally, we will refer to the container ID, put the map on the element

container

and display it on the page using the function

draw()

.

map.container('container');
map.draw();

This should be enough to display our flow map on a web page. However, since we are working with a world map, it will be useful for the convenience of users to add the ability to change the scale.

And it’s not difficult at all – it only takes three additional lines of code.

var zoomController = anychart.ui.zoom();
zoomController.target(map);
zoomController.render();

That’s all! Our basic flow map is ready.

Take a look at her interactive version and play around with her. AnyChart sandbox. Here is the entire source code:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>JavaScript Flow Map</title>
    <script src="https://cdn.anychart.com/releases/8.11.0/js/anychart-core.min.js"></script>
    <script src="https://cdn.anychart.com/releases/8.11.0/js/anychart-map.min.js"></script>
    <script src="https://cdn.anychart.com/releases/8.11.0/geodata/custom/world/world.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.8.0/proj4.js"></script>
    <script src="https://cdn.anychart.com/releases/8.11.0/js/anychart-ui.min.js"></script>
    <link rel="stylesheet" href="https://cdn.anychart.com/releases/8.11.0/css/anychart-ui.min.css">
    <link rel="stylesheet" href="https://cdn.anychart.com/releases/8.11.0/fonts/css/anychart-font.min.css">
    <style type="text/css">      
      html, body, #container {
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
      }
    </style>
  </head>
  <body>
    <div id="container"></div>
    <script>
      anychart.onDocumentReady(function () {
        // Установка данных целевых стран.
        var data = [
          { points: [28.610198, 77.207584, -30.592659, 145.943667], to: "Australia", total: 100009 },
          { points: [28.610198, 77.207584, 45.411673, -75.69629], to: "Canada", total: 183310 },
          { points: [28.610198, 77.207584, 52.511693, 13.403121], to: "Germany", total: 34864 },
          { points: [28.610198, 77.207584, 23.5880, 58.3829], to: "Oman", total: 39550 },
          { points: [28.610198, 77.207584, 25.2854, 51.5310], to: "Qatar", total: 46000 },
          { points: [28.610198, 77.207584, 55.747362, 37.621273], to: "Russian Federation", total: 18039 },
          { points: [28.610198, 77.207584, 24.710437, 46.675164], to: "Saudi Arabia", total: 65800 },
          { points: [28.610198, 77.207584, 24.4539, 54.3773], to: "United Arab Emirates", total: 164000 },
          { points: [28.610198, 77.207584, 52.667078, -0.955920], to: "United Kingdom", total: 55465 },
          { points: [28.610198, 77.207584, 38.884053, -77.033513], to: "United States", total: 465791 },
        ]
        // Создание графика карты.
        var map = anychart.connector();
        // Добавление геоданных карты мира.
        map.geoData(anychart.maps.world);
        // Создание соединительных линий.
        var connectorSeries = map.connector(data);
        // Установка меток для соединительных линий.
        connectorSeries
          .labels()
          .enabled(true)
          .position('100%')
          .format(function () {
            return this.getData('to')
          });
        // Установка стрелочек в конце линий.
        connectorSeries
          .markers()
          .position('100%')
          .size(12);
        // Добавление возможности масштабирования.
        var zoomController = anychart.ui.zoom();
        zoomController.target(map);
        zoomController.render();
        // Установка названия карты.
        map.title('Top 10 Destination Countries for Indian Students Studying Abroad');
        // Установка контейнера.
        map.container('container');
        // Отрисовка карты.
        map.draw();
      });
    </script>
  </body>
</html>

Customization

Great job! Creating simple interactive flow maps with JS is really not that hard. Now let’s move on to the next steps to improve and personalize our map, making it more informative and attractive.

Next, we will make some simple changes to the code to add additional features and customization options to the map. Thus, we will increase the interest of users and present information more effectively.

▍ A. Optimizing Curved Lines and Label Positions

In our flow map, information about Indian students in 10 countries is represented by connecting lines. However, with the data we are using, the presets tend to overlap the lines, and some country names are not visible until zoomed in.

To fix this, you can change the dataset by adding the property curvature, which will allow you to correct the curvature of individual lines. In addition, we will add a property label and set its values ​​so that the marks are displayed at the desired points on the map.

var data = [
  { points: [28.610198, 77.207584, -30.592659, 145.943667], to: "Australia", total: 100009, curvature: 0.5 },
  { points: [28.610198, 77.207584, 45.411673, -75.69629], to: "Canada", total: 183310, curvature: 0.8, label: { offsetY: -30 } },
  { points: [28.610198, 77.207584, 52.511693, 13.403121], to: "Germany", total: 34864, curvature: 0.3, label: { anchor: 'center-top', offsetY: -2 } },
  { points: [28.610198, 77.207584, 23.5880, 58.3829], to: "Oman", total: 39550, curvature: -0.5, label: { anchor: 'left-top' } },
  { points: [28.610198, 77.207584, 25.2854, 51.5310], to: "Qatar", total: 46000, curvature: 0.4, label: { anchor: 'right-top', offsetY: -20 } },
  { points: [28.610198, 77.207584, 55.747362, 37.621273], to: "Russian Federation", total: 18039, curvature: 0.4, label: { anchor: 'left-bottom' } },
  { points: [28.610198, 77.207584, 24.710437, 46.675164], to: "Saudi Arabia", total: 65800, curvature: 0.7, label: { offsetY: -5 } },
  { points: [28.610198, 77.207584, 24.4539, 54.3773], to: "United Arab Emirates", total: 164000, curvature: 0, label: { anchor: 'left-top', offsetY: -15 } },
  { points: [28.610198, 77.207584, 52.667078, -0.955920], to: "United Kingdom", total: 55465, curvature: 0.4, label: { anchor: 'right-top', offsetY: -25, offsetX: -10 } },
  { points: [28.610198, 77.207584, 38.884053, -77.033513], to: "USA", total: 465791, curvature: -0.6 },
];

With these changes, it is now possible to display labels even if they overlap. This can be done with one line of code.

map.overlapMode("allow-overlap");

▍ B. Refinement of background information

Now the map help shows latitude and longitude, which do not have much meaning. To make it more informative, we will display more relevant information.

One option is to show the name of the target country and the total number of students in the help. Thus, users can easily understand the information presented and better navigate the map.

connectorSeries
  .tooltip()
  .useHtml(true)
  .format(function () {
    return (
      '<h5 style="font-size:12px; font-weight:400; margin: 0.25rem 0;">To:<b> ' + this.getData('to') + '</b></h5>' +
      '<h5 style="font-size:12px; font-weight:400; margin: 0.25rem 0;">Total Students: <b>' + this.getData('total').toLocaleString() + '</b></h5>'
    );
  });

▍ C. Header development

To increase the appeal of our map title, we can highlight it with HTML styling so that it is more visible and attracts attention.

map.title()
  .enabled(true)
  .useHtml(true)
  .text(
    '<span style="font-size:15px;font-weight:600;">Top 10 Destination Countries for Indian Students Studying Abroad</span><br/>' +
    '<span style="font-size: 14px;">The top destination is the U.S.</span>'
  );

▍ D. Selecting colors for connectors and markers

To make some aesthetic changes, you can replace the base color of the lines with a gradient that will smoothly transition from red to yellow.

To do this, we will change the code that creates the lines, and apply a new color to them using the functions fill() And stroke().

var connectorSeries = map.connector(data)
  .fill(['#e8dd09', 'red'])
  .stroke(['#e8dd09', 'red']);
connectorSeries
  .hovered()
  .stroke('#808080')
  .fill('#808080');
connectorSeries
  .hovered()
  .markers()
  .stroke('#808080')
  .fill('#808080');

After this change, the map will look more attractive and interesting. Although if you prefer other colors, you can experiment with different combinations of them.

▍ E. Change the color and thickness of lines based on data

In the final customization step, we’ll make the connectors more interesting by changing their colors and thickness based on the number of students, as well as adding a legend for better understanding.

To do this, we will first group countries into four categories based on the total number of students enrolled in them.

  • less than 50,000;
  • from 50,000 to 100,000;
  • from 100,000 to 200,000;
  • over 200,000.

We will also create

filter_function()

which will filter the data according to these categories.

function filter_function(val1, val2) {
  if (val2)
    return function (fieldVal) {
      return val1 <= fieldVal && fieldVal < val2;
    };
    else
      return function (fieldVal) {
        return val1 <= fieldVal;
      };
}

Next, we will create a function

createSeries()

which will receive datasets and configurations, creating connecting lines based on them.

function createSeries(data, name, color, size) {

  // Создание соединительных линий.
  var connectorSeries = map.connector(data)
    .name(name)
    .fill(color)
    .stroke(color)
    .color(color);
  connectorSeries
    .hovered()
    .stroke('#808080')
    .fill('#808080');
  connectorSeries
    .hovered()
    .markers()
    .stroke('#808080')
    .fill('#808080');

  // Установка меток для линий.
  connectorSeries
    .labels()
    .enabled(true)
    .position('100%')
    .fontColor('#2D2D2D')
    .format(function () {
      return this.getData('to')
    });

  // Установка стрелочек в конце линий.
  connectorSeries
    .markers()
    .position('100%')
    .size(12);

  // Настройка справочной информации для линий.
  connectorSeries
    .tooltip()
    .useHtml(true)
    .format(function () {
      return (
        '<h5 style="font-size:12px; font-weight:400; margin: 0.25rem 0;">To:<b> ' + this.getData('to') + '</b></h5>' +
        '<h5 style="font-size:12px; font-weight:400; margin: 0.25rem 0;">Total Students: <b>' + this.getData('total').toLocaleString() + '</b></h5>'
      );
    });

  // Установка толщины линии, исходя из количества студентов.
  connectorSeries
     .startSize(size[0])
     .endSize(size[1]);
}

We will also create a dataset from our data.

var dataSet = anychart.data.set(data).mapAs();

Then we filter it with

filter_function()

and pass it to the function

createSeries()

to create a set of lines.

createSeries(dataSet.filter('total', filter_function(0, 50000)), 'Less than 50,000', '#A149FA', [1, 0]);

createSeries(dataSet.filter('total', filter_function(50000, 100000)), '50,000 - 100,000', '#3B44F6', [2, 1]);

createSeries(dataSet.filter('total', filter_function(100000, 200000)), '100,000 - 200,000', '#3EC70B', [4, 1]);

createSeries(dataSet.filter('total', filter_function(200000, 500000)), 'More than 200,000', '#F7EC09', [6, 1]);

Finally, we’ll add a legend to our flow map to help users understand what the colors and thickness of the connectors mean.

map.legend().enabled(true).position('bottom').padding([20, 0, 0, 0]).fontSize(10);

map.legend().title().enabled(true).text('Number of Students').fontSize(13).padding([0, 0, 5, 0]);

So the goal has been reached! We have created a beautiful interactive flow map using JS.

As a result, we can see that the colors and thickness of the connecting lines vary based on the number of students. The final interactive version of this map can be viewed at

AnyChart sandbox

. You can also make additional changes to the code and experiment with it.

Here is the whole map code:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>JavaScript Flow Map</title>
    <script src="https://cdn.anychart.com/releases/8.11.0/js/anychart-core.min.js"></script>
    <script src="https://cdn.anychart.com/releases/8.11.0/js/anychart-map.min.js"></script>
    <script src="https://cdn.anychart.com/releases/8.11.0/geodata/custom/world/world.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.8.0/proj4.js"></script>
    <script src="https://cdn.anychart.com/releases/8.11.0/js/anychart-ui.min.js"></script>
    <link rel="stylesheet" href="https://cdn.anychart.com/releases/8.11.0/css/anychart-ui.min.css">
    <link rel="stylesheet" href="https://cdn.anychart.com/releases/8.11.0/fonts/css/anychart-font.min.css">
    <style type="text/css">      
      html, body, #container {
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
      }
    </style>
  </head>
  <body>
    <div id="container"></div>
    <script>
anychart.onDocumentReady(function () {
  // Установка данных для целевых стран.
  var data = [
    { points: [28.610198, 77.207584, -30.592659, 145.943667], to: "Australia", total: 100009, curvature: 0.5 },
    { points: [28.610198, 77.207584, 45.411673, -75.69629], to: "Canada", total: 183310, curvature: 0.8, label: {offsetY: -30} },
    { points: [28.610198, 77.207584, 52.511693, 13.403121], to: "Germany", total: 34864, curvature: 0.3, label: {anchor: 'center-top', offsetY: -2} },
    { points: [28.610198, 77.207584, 23.5880, 58.3829], to: "Oman", total: 39550, curvature: -0.5, label: {anchor: 'left-top'} },
    { points: [28.610198, 77.207584, 25.2854, 51.5310], to: "Qatar", total: 46000, curvature: 0.4, label: {anchor: 'right-top', offsetY: -20} },
    { points: [28.610198, 77.207584, 55.747362, 37.621273], to: "Russian Federation", total: 18039, curvature: 0.4, label: {anchor: 'left-bottom'} },
    { points: [28.610198, 77.207584, 24.710437, 46.675164], to: "Saudi Arabia", total: 65800, curvature: 0.7, label: {offsetY: -5} },
    { points: [28.610198, 77.207584, 24.4539, 54.3773], to: "United Arab Emirates", total: 164000, curvature: 0, label: {anchor: 'left-top', offsetY: -15} },
    { points: [28.610198, 77.207584, 52.667078, -0.955920], to: "United Kingdom", total: 55465, curvature: 0.4, label: {anchor: 'right-top', offsetY: -25, offsetX: -10} },
    { points: [28.610198, 77.207584, 38.884053, -77.033513], to: "USA", total: 465791, curvature: -0.6 },
  ]
  // Создание графика карты.
  var map = anychart.connector();
  // Создание датасета из данных.
  var dataSet = anychart.data.set(data).mapAs();
  // Добавление геоданных карты мира.
  map.geoData(anychart.maps.world);
  // Создание 4 наборов и фильтрация данных по количеству студентов
  createSeries(dataSet.filter('total', filter_function(0, 50000)), 'Less than 50,000', '#A149FA', [1, 0]);
  createSeries(dataSet.filter('total', filter_function(50000, 100000)), '50,000 - 100,000', '#3B44F6', [2, 1]);
  createSeries(dataSet.filter('total', filter_function(100000, 200000)), '100,000 - 200,000', '#3EC70B', [4, 1]);
  createSeries(dataSet.filter('total', filter_function(200000, 500000)), 'More than 200,000', '#F7EC09', [6, 1]);
  // Функция для создания и настройки наборов.
  function createSeries(data, name, color, size) {
    // Создание соединительных линий.
    var connectorSeries = map.connector(data)
      .name(name)
      .fill(color)
      .stroke(color)
      .color(color);
    connectorSeries
      .hovered()
      .stroke('#808080')
      .fill('#808080');
    connectorSeries
      .hovered()
      .markers()
      .stroke('#808080')
      .fill('#808080');
    // Установка меток для линий.
    connectorSeries
      .labels()
      .enabled(true)
      .position('100%')
      .fontColor('#2D2D2D')
      .format(function () {
        return this.getData('to')
      });
    // Установка стрелочек в конце линий.
    connectorSeries
      .markers()
      .position('100%')
      .size(12);
    // Указание справочной информации для наборов.
    connectorSeries
      .tooltip()
      .useHtml(true)
      .format(function () {
        return (
          '<h5 style="font-size:12px; font-weight:400; margin: 0.25rem 0;">To:<b> ' + this.getData('to') + '</b></h5>' +
          '<h5 style="font-size:12px; font-weight:400; margin: 0.25rem 0;">Total Students: <b>' + this.getData('total').toLocaleString() + '</b></h5>'
          );
      });
    // Установка толщины соединительной линии, исходя из количества студентов.
    connectorSeries
      .startSize(size[0])
      .endSize(size[1]);
  }
  // Добавление возможности масштабирования.
  var zoomController = anychart.ui.zoom();
  zoomController.target(map);
  zoomController.render();
  // Отображение всех меток, даже в случае наложения.
  map.overlapMode("allow-overlap");
  // Установка названия карты.
  map.title()
    .enabled(true)
    .useHtml(true)
    .text(
      '<span style="font-size:15px;font-weight:600;">Top 10 Destination Countries for Indian Students Studying Abroad</span><br/>' +
      '<span style="font-size: 14px;">The top destination is the U.S.</span>'
    );
  // Добавление легенды.
  map.legend().enabled(true).position('bottom').padding([20, 0, 0, 0]).fontSize(10);
  map.legend().title().enabled(true).text('Number of Students').fontSize(13).padding([0, 0, 5, 0]);
  // Установка контейнера.
  map.container('container');
  // Отрисовка карты.
  map.draw();
});
// Функция фильтрации данных.
function filter_function(val1, val2) {
  if (val2)
    return function (fieldVal) {
      return val1 <= fieldVal && fieldVal < val2;
    };
  else
    return function (fieldVal) {
      return val1 <= fieldVal;
    };
}
    </script>
  </body>
</html>

Conclusion

Creating flow maps with JavaScript is easy and fun. I recommend you check out

other examples of cards and connecting lines

from which you can draw ideas for your own map.

Good luck!

Telegram channel with prize draws, IT news and posts about retro games 🕹️

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *