This post is written by Mina Pêcheux
This post has been updated in July 2022 to introduce some fixes to the codemagic.yaml file in the sample project, and to reflect that you no longer need to contact Codemagic solution engineers to set up Unity machines for you: You start using Unity on Codemagic right away, as it is preinstalled on Mac, Linux and Windows build machines.
Let’s see how to share our Unity Android game projects with trusted fellow developers automatically! Recently, I started to work on a little prototype Unity mobile game called Slash’n’crack, in which you have to swipe the screen to slash and destroy asteroids. For me, this project has been a nice opportunity to brush up on my Unity/Android skills and share some game dev tips and tricks through a series of gamedev log articles.
There is, however, something that always bothered me about developing Unity mobile apps — actually building and sharing them. Unity makes it easy to code for Android, but it’s not always on par with other frameworks when it comes to distributing your product… especially if you want to automate this process.
Today, I want to discuss why and how we can combine Unity with two really cool tools, Codemagic and Firebase, to set up a CI/CD process and automate building and publishing a Unity Android project like Slash’n’crack. So, in this article, I’ll describe:
- Why it’s interesting and useful to automate the distribution of a Unity app.
- How to automate the build process for a Unity Android app using Codemagic and use this CI/CD workflow to generate a build artifact.
- How to take advantage of Firebase App Distribution to share this artifact with a list of handpicked testers.
- How to upgrade the Codemagic workflow so that you can directly publish the artifacts to the Firebase project and have a fully automated distribution process!
If you’re already familiar with Codemagic workflows, feel free to jump directly to the second part about Firebase distribution!
Otherwise, let’s dive in. 🙂
Foreword: Why automate the distribution of your Unity app?
Before actually talking about the how, let’s just take a moment to pause and discuss why it’s interesting to automate the distribution of your Unity project.
As brilliantly explained in this 2020 GDC conference talk by Seth Coster, DevOps and its core tool, automation, are the key to setting up a sound and sane development process for any kind of dev project.
You know all these articles that keep on talking about big game companies crunching their employees for months and leaving them barely alive on the side of the road? This is continuing to happen mainly because the game industry has gotten used to weird or even dangerous work processes that force their workers to keep on pushing their limits!
Nowadays, there are plenty of tools and tech that can help sanitize and improve those processes so that you’re not condemned to working 80 hours a week for three months just because your game released with a bunch of bugs.
Automating the distribution of your Unity projects is a great way to keep your QA team, beta testers, and/or investors informed of what’s going on and create momentum around your new game. You will also get almost instant feedback, which means that you will be able to integrate this feedback into your development process quickly and directly benefit from it. This is far better than waiting for the day of the release and then being flooded by an avalanche of bugs and having to spend nightmarish hours making day-one patches!
By delivering updates continuously and in small batches (which is typically what good commit-based processes rely on), you avoid waiting on reviews and reduce the number of unfinished projects: Each task is better scoped and can thus be tackled entirely before moving on to the next one.
Automation also allows you to scale up more easily because it removes manual steps. Manual steps are performed by team members — the more manual steps there are, the more time they require. Having too many manual steps in your process sets a hard limit on how much you can produce unless you expand your team! Automation allows the team to remove this burden and focus on those tasks that cannot be automated, e.g., game design and development.
When you go one step further and start to monitor your apps, automating distribution also means that your testers and QA will feed you with data continuously, and you’ll get live reports on all the relevant metrics you prepared. Pretty cool, right? 😉
All of this shows how valuable automation can be — no matter how big or small your dev team is!
Part 1: Automating the Unity Android build with Codemagic
Codemagic is a powerful online tool that allows you to quickly set up CI/CD by connecting your Git repositories and completing just a few configuration steps. It’s also a convenient way to get access to specific hardware: With Codemagic, you can easily build your Unity game for Mac, iOS, Windows, Android, etc. The service scales well in terms of price and makes it simple to publish your project on the App Store or Google Play.
For the purposes of this article, we’re mostly interested in the ability to build for various target platforms and easily link with distributors. We’ll also see that configuring the Codemagic app is super quick and user friendly. 🙂
Important note: At the time of writing this, deploying Unity apps with Codemagic requires a Pro or Plus Unity license. Previously, you would’ve also needed to contact Codemagic so that we could give you access to the special type of build machines with Unity, but this is no longer necessary, as Unity is preinstalled by default on Mac, Linux, and Windows build machines. However, you can still contact Codemagic to receive a personal demo.
Preparing your Unity project for Codemagic integration
Codemagic works with Git repositories. This means that the first step is to make sure your Unity project is using a Git source control service, like GitHub, Bitbucket, or GitLab (or something else, as long as it’s a Git service). I’ve covered how to set up Unity to work with Git in a separate blog post, so make sure to check it out if you’re not familiar with this process. 🙂
In my case, I decided to version (and share) the code of my Slash’n’crack game on GitHub over here. 🚀
Now, log into your Codemagic account, and make sure that you allowed the Codemagic integration in your Git provider and that you gave it access to your repo…
Now, let’s make sure our Unity project is properly configured for Android builds!
- In the “File > Build Settings” panel, make sure Android is selected as the build platform.
- In “Edit > Project Settings”, select the “Player” tab, and check that the Minimum API Level and Target API Level are both set to Android 11.0 (API level 30), that the Scripting Backend is IL2CPP, and that ARMv7 and ARM64 are enabled in the “Target Architectures”.
- In the same tab, open the “Publishing Settings” block at the end, and check the “Custom Base Gradle Template” option. Then, open the newly created
Assets/Plugins/Android/baseProjectTemplate.gradle
and replace its contents with the following:
// GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN
allprojects {
buildscript {
repositories {**ARTIFACTORYREPOSITORY**
google()
mavenCentral()
}
dependencies {
// If you are changing the Android Gradle Plugin version, make sure it is compatible with the Gradle version preinstalled with Unity
// See which Gradle version is preinstalled with Unity here https://docs.unity3d.com/Manual/android-gradle-overview.html
// See official Gradle and Android Gradle Plugin compatibility table here https://developer.android.com/studio/releases/gradle-plugin#updating-gradle
// To specify a custom Gradle version in Unity, go to "Preferences > External Tools", uncheck "Gradle Installed with Unity (recommended)" and specify a path to a custom Gradle version
classpath 'com.android.tools.build:gradle:4.0.1'
**BUILD_SCRIPT_DEPS**
}
}
repositories {**ARTIFACTORYREPOSITORY**
google()
mavenCentral()
flatDir {
dirs "${project(':unityLibrary').projectDir}/libs"
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Note: Some of those settings are really only required for Google Play distribution and .aab
bundling. We’ll see in a second that we won’t be diving into .aab
and will instead be sticking with .apk
, but it can’t hurt to be up to date… 😉
Next, we have to add a C# script to our project in the Assets/Editor/
folder, Build.cs
:
using System.Linq;
using System;
using UnityEditor;
using UnityEngine;
public static class BuildScript
{
[MenuItem("Build/Build Android")]
public static void BuildAndroid()
{
PlayerSettings.Android.useCustomKeystore = true;
EditorUserBuildSettings.buildAppBundle = false;
// Set bundle version. NEW_BUILD_NUMBER environment variable is set in the codemagic.yaml
var versionIsSet = int.TryParse(Environment.GetEnvironmentVariable("NEW_BUILD_NUMBER"), out int version);
if (versionIsSet)
{
Debug.Log($"Bundle version code set to {version}");
PlayerSettings.Android.bundleVersionCode = version;
}
else
{
Debug.Log("Bundle version not provided");
}
// Set keystore name
string keystoreName = Environment.GetEnvironmentVariable("CM_KEYSTORE_PATH");
if (!String.IsNullOrEmpty(keystoreName))
{
Debug.Log($"Setting path to keystore: {keystoreName}");
PlayerSettings.Android.keystoreName = keystoreName;
}
else
{
Debug.Log("Keystore name not provided");
}
// Set keystore password
string keystorePass = Environment.GetEnvironmentVariable("CM_KEYSTORE_PASSWORD");
if (!String.IsNullOrEmpty(keystorePass))
{
Debug.Log("Setting keystore password");
PlayerSettings.Android.keystorePass = keystorePass;
}
else
{
Debug.Log("Keystore password not provided");
}
// Set keystore alias name
string keyaliasName = Environment.GetEnvironmentVariable("CM_KEY_ALIAS");
if (!String.IsNullOrEmpty(keyaliasName))
{
Debug.Log("Setting keystore alias");
PlayerSettings.Android.keyaliasName = keyaliasName;
}
else
{
Debug.Log("Keystore alias not provided");
}
// Set keystore password
string keyaliasPass = Environment.GetEnvironmentVariable("CM_KEY_PASSWORD");
if (!String.IsNullOrEmpty(keyaliasPass))
{
Debug.Log("Setting keystore alias password");
PlayerSettings.Android.keyaliasPass = keyaliasPass;
}
else
{
Debug.Log("Keystore alias password not provided");
}
BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();
buildPlayerOptions.locationPathName = "android/android.apk";
buildPlayerOptions.target = BuildTarget.Android;
buildPlayerOptions.options = BuildOptions.None;
buildPlayerOptions.scenes = GetScenes();
Debug.Log("Building Android");
BuildPipeline.BuildPlayer(buildPlayerOptions);
Debug.Log("Built Android");
}
private static string[] GetScenes()
{
return (from scene in EditorBuildSettings.scenes where scene.enabled select scene.path).ToArray();
}
}
This script will allow Codemagic to run Unity via the command line and build the project for Android programmatically rather than using user interfaces as we normally do in the editor. Once you’ve added it to your project, don’t forget to commit it!
You might notice two things:
- We are making an
.apk
build. Unity allows for two types of Android app packaging: a simple package (.apk
) or a Google Play Android App Bundle (.aab
). The latter is specifically meant for Google Play distribution, but since I plan on sticking with Firebase for now, I don’t want to dive into this complexity. Therefore, I’ll be using the.apk
format. - This script has plenty of references to “keystore” variables. But what are those?
Well, that’s actually related to the last thing we need to prepare for our project: a keystore credentials file. As explained in the Android developer docs, this type of file is a safe way of storing cryptographic keys in a container to better protect your app.
The easiest method to generate a new keystore file is with the keytool
utility; it is provided with Java, so you most likely already have it on your computer, but just in case you don’t, here is a short post explaining how to install it.
You can call keytool
with the following options to get a nice new keystore file (remember to adapt the parts between the <
and >
signs):
keytool -genkey -v -keystore <keystore_name>.keystore -alias <alias_name> -keyalg RSA -keysize 2048 -validity 10000
After running this command, you will be prompted to answer a few questions — you’ll need to pick some passwords and other settings. In particular, these “passwords” are actually the private and public keys of your cryptographic key pair. You don’t need to know all the intricate details, but just make sure to remember the “keystore password” and “key password” you pass the tool because we’ll need them very soon when we set up our Codemagic app.
Important note: These keystore credentials should NEVER be committed to the Git repo. Add them to your .gitignore
to make sure you don’t add them by accident. 🙂
The two following steps are to add the Unity project to Codemagic and to configure the CI/CD to build our Android artifact. You can find the details on how to do this in another post on the Codemagic blog, but I’ll quickly go through the process here as well, paying some additional attention to those keystore variables. If you are already familiar with this, feel free to skip the next two sections and jump to the most interesting part — enabling Firebase Distribution.
Adding your Unity project to Codemagic
The next step is to actually add your Unity project to Codemagic. To do so, head over to your Codemagic dashboard and click the “Add application” button:
There, you can pick your Git provider and the repository from which to fetch the code:
Finally, you can choose your Git repo in the list and click the “Add application” button.
Setting up the environment variables
Once you’ve added your app, you’ll be taken to its configuration panel, and you’ll be able to edit its settings. The first tab shows you your main Codemagic configuration file, codemagic.yaml
, if you have one. We haven’t created one yet, so we don’t have anything here, and we can’t start a build.
But before we add this config file, we need to prepare our app environment so that it has all the necessary data when it tries to build our Unity project. Since everything is done on a remote computer, remember that it doesn’t “know” a thing at the beginning — it’s not like your local dev environment, as it’s blank (save for the Unity SDK and some other tools). So you have to feed the app all the relevant info to have it properly execute and build your code. 🙂
This is mostly done via the definition of environment variables — these can be accessed in the second tab of your app’s settings. The UI will allow you to add a whole bunch of key-value pairs, each of which corresponds to one environment variable.
These variables can be safely encrypted, and they are a very useful way to define secrets or credentials without writing them explicitly anywhere… or, even worse, committing them to your public Git repo! 😉
Here, we’ll need to define two groups of environment variables — unity
and keystore_credentials
(see below for more info on each variable):
To define a new group, simply start typing it in the “Select group” field on the right, and you’ll be able to add it; if the group already exists, select it from the list to add your new variable to it.
Make sure to keep the “Secure” checkbox toggled on so that all your variables are properly encrypted and safely stored.
OK — we now have a total of eight variables:
UNITY_PASSWORD
UNITY_USERNAME
UNITY_SERIAL
CM_KEYSTORE
CM_KEY_PASSWORD
CM_KEY_ALIAS
CM_KEYSTORE_PATH
CM_KEYSTORE_PASSWORD
The first three are your Unity credentials, which allow the workflow to connect and use the Unity command-line tool to build your app. Remember that you need a Pro or Plus Unity license to use Codemagic’s Unity CI/CD.
The other variables are various pieces of info on our keystore credentials file.
CM_KEY_PASSWORD
and CM_KEYSTORE_PASSWORD
are your private and public keys; CM_KEY_ALIAS
is the alias name of your file; CM_KEYSTORE_PATH
is the path to the file on Codemagic’s build computer — you need to set it to:
CM_KEYSTORE_PATH = keystore.keystore
Finally, the CM_KEYSTORE
variable is the actual contents of your keystore file. To add it to Codemagic, you first need to encode it in Base64 format, and then you can copy this encoded version into the “value” field.
Adding the Codemagic configuration file
Now that our app has the proper environment, we need to tell Codemagic what exactly to do with our code. 🙂
In other words, we need to define the workflow we want to run with the help of the aforementioned codemagic.yaml
config file. This YAML settings file allows us to list all the CI/CD steps we want Codemagic to go through when we start a build in our dashboard.
Here, we’ll need to:
- set up our environment using the variables we just defined;
- activate the Unity license using our Unity credentials;
- set up the keystore;
- build the project as an
.apk
artifact; - deactivate the Unity license on a Codemagic machine.
This can be done fairly easily with the following codemagic.yaml
file placed at the root of the Git repository (i.e., next to the Assets/
folder):
workflows:
unity-android-workflow:
name: Unity Android Workflow
max_build_duration: 120
environment:
groups:
- unity # <-- (Includes UNITY_HOME, UNITY_SERIAL, UNITY_USERNAME and UNITY_PASSWORD)
- keystore_credentials # <-- (Includes CM_KEYSTORE, CM_KEYSTORE_PASSWORD, CM_KEY_PASSWORD, CM_KEY_ALIAS)
vars:
UNITY_BIN: ${UNITY_HOME}/Contents/MacOS/Unity
BUILD_SCRIPT: BuildAndroid
PACKAGE_NAME: "com.minapecheux.SlashAndCrack"
scripts:
- name: Activate Unity License
script: |
$UNITY_BIN -batchmode -quit -logFile -serial ${UNITY_SERIAL?} -username ${UNITY_USERNAME?} -password ${UNITY_PASSWORD?}
- name: Set up keystore
script: |
echo $CM_KEYSTORE | base64 --decode > $CM_BUILD_DIR/keystore.keystore
- name: Set build number and export Unity
script: |
export NEW_BUILD_NUMBER=$(($(google-play get-latest-build-number --package-name "$PACKAGE_NAME" --tracks=alpha) + 1))
$UNITY_BIN -batchmode -quit -logFile -projectPath . -executeMethod BuildScript.$BUILD_SCRIPT -nographics
artifacts:
- android/*.apk
publishing:
scripts:
- name: Deactivate Unity License
script: $UNITY_BIN -batchmode -quit -returnlicense -nographics
As you can see, I first use environment variables to “re-import” all my variables in the environment
block. I also declare a few additional ones:
UNITY_BIN
contains the path to the Unity executable on the build machine. Refer to the Codemagic docs to learn what to write here depending on the machine you plan on using (Mac, Windows, or Linux).BUILD_SCRIPT
is the name of the method to run to build our Unity project programmatically: It’s theBuildAndroid()
method we implemented inBuild.cs
earlier.PACKAGE_NAME
is the package name of the Android app that is created automatically by Unity based on your current Project Settings, but it can be re-configured if you want.
To change the package name, simply go to the Unity editor and open the “Edit > Project Settings” menu. Then, select the “Player” tab. You then have two ways of modifying the package name.
Firstly, you can simply change the Company Name and Product Name settings at the very top:
Alternatively, you can explicitly override the auto-generated package name by scrolling down to the “Other Settings” block and selecting the corresponding option:
This package name is important — we’ll reuse it later on when we set up Firebase App Distribution, so make sure to configure it properly and remember it. 😉
The next block in my codemagic.yaml
file is the scripts
: These contain the various commands to run to enable the Unity license and the keystore and to then build the Unity Android app.
Next, we have the artifacts
: These are the files that are produced during the workflow and that should be exported so that we can retrieve them once the build machine is revoked.
Finally, the publishing
block tells the workflow what to do with these artifacts, i.e., after the build. For now, we simply deactivate the Unity license, but very soon, we’ll see how to publish our new .apk
file to Firebase directly.
Once you’ve added this YAML config file to the root of your Git repo, don’t forget to commit it. 🙂 Codemagic automatically detects codemagic.yaml
and knows what to do!
Testing it out and getting your Android app artifact
All that’s left to do is check that everything works properly and that we can indeed retrieve an .apk
build thanks to our Codemagic workflow!
So, let’s just click the “Start new build” button and let the magic happen…
After a while, you should see that your workflow completed all the steps we defined and that it produced an artifact, the android.apk
file:
That means we’ve successfully automated the build of our Unity Android app with Codemagic, and we’re now ready to set up the Firebase distribution!
Part 2: Adding automatic publishing with Firebase App Distribution
Creating your Firebase project
Firebase is a Google product that helps you package and share apps (whether they are iOS, Android, web projects, etc.) and offers a palette of tools to monitor them. It allows you to automate releases and can engage with your testers or consumers to better grow your business.
Since Firebase is made by Google, you can use it by logging into it with your Google account.
You’ll then get to the Firebase dashboard, where you can create a new project or select an existing one:
Creating a project is basically just a matter of giving it a name and choosing some pre-configured tools — the process is pretty straightforward. I personally didn’t add any analytics or metrics for now to avoid overloading my project (and my brain…) with too many parameters at a time. After all, we’re here to learn about Firebase App Distribution.
Adding the Android app and getting your credentials
Once you’re done, you’ll get to another dashboard, this time one dedicated to your Firebase project:
Again, from here, you’ll be able to add or access the app(s) in your Firebase project. Firebase lets you add four types of apps: Android, iOS, web, or Unity.
Since we are using Codemagic to handle the build part, we don’t want to choose Unity but rather Android — don’t forget that our Codemagic workflow will prepare everything and feed Firebase the .apk
Android artifact directly. 🙂
Click on the Android app button to add a new Android app, and you will get a new config screen, where you’ll need to enter your Android package name (the one we set earlier in Unity!).
The second step will allow you to get your Firebase Google credentials for this app as a JSON file, google-services.json
:
You need to download this JSON file on your computer because we’ll soon need to read and copy its content. But just like with the keystore credentials, make sure to add this file to your .gitignore
so that it is NOT committed if you move it to your Git repo folder locally on your computer.
Then, go to your Firebase project settings, and scroll down to the Android apps section. Here, you’ll be able to find the unique ID of your new Android app, which I’ll call the “Android app ID” later on.
The last piece of info we need to link our Codemagic workflow to Firebase is the Firebase token so that Codemagic is allowed to run some Firebase commands for us and publish our artifact automatically.
To get this, we need to do the following on our local computer:
- install the Firebase CLI tools;
- in the shell, run the CI/CD login command:
firebase login:ci
; - this opens a browser where we can connect with our Google account to authorize the owners of the token to access my Firebase account;
- once we’re authenticated, we can go back to the shell and get our Firebase token.
Setting up Firebase App Distribution and testers
My goal here is to make it easy to share my game with others — so I want to take advantage of Firebase App Distribution, which allows you to quickly distribute an app and all its updated versions to a list of trusted testers or some other list of people.
To enable it, just go to the “App Distribution” menu on the left in your Firebase project and add your Android app. This will give you access to another panel that lists all the versions of this app and lets you upload a new .apk
or .aab
(if you’ve linked the app to Google Play) manually or automatically to create a new version — a new release:
Important note: To upload a new version of the app (either manually or automatically), you need to make sure that its build version has been bumped compared to the previous one — you can’t have two different versions of the app with the same number! 😉
And now, we can go to the “Testers” tab and define our own tester groups. This way, when we publish a new version of our Android app, all the testers in the chosen groups will get an email telling them about the update with a link to the new iteration!
By the way, don’t forget to pinpoint the “slug” of your group (see the blue arrow in the picture above) after you’ve added it so that you can pass it on to the Codemagic workflow later on…
Adding the required environment variables in Codemagic
At this point, we have the three required files/variables we need to link Firebase App Distribution to Codemagic: the JSON credentials, the Android app ID, and the Firebase token.
Like before, we’ll add these as environment variables in our Codemagic app in a new group called “firebase”:
The three variables are named ANDROID_FIREBASE_SECRET
, FIREBASE_APP_ID
, and FIREBASE_TOKEN
. For the JSON file, remember to encode the contents in Base64 format and then copy the encoded result as the value for the ANDROID_FIREBASE_SECRET
variable.
Updating the Codemagic configuration file
The final step is to update our codemagic.yaml
file to publish the .apk
artifact directly as a new version of our Firebase Android app. We’ll import this new environment variable group into our workflow and add Firebase publishing at the end of the publishing
block:
workflows:
unity-android-workflow:
name: Unity Android Workflow
max_build_duration: 120
environment:
groups:
- unity # <-- (Includes UNITY_HOME, UNITY_SERIAL, UNITY_USERNAME, and UNITY_PASSWORD)
- keystore_credentials # <-- (Includes CM_KEYSTORE, CM_KEYSTORE_PASSWORD, CM_KEY_PASSWORD, CM_KEY_ALIAS)
- firebase # <-- (Includes FIREBASE_APP_ID, FIREBASE_TOKEN)
vars: ...
scripts: ...
artifacts: ...
publishing:
scripts: ...
firebase:
firebase_token: $FIREBASE_TOKEN
android:
app_id: $FIREBASE_APP_ID
groups: # Add one or more groups that you wish to distribute your Android application to — you can create groups in the Firebase console
- happy-testers
I’ve used the slug of my “Happy Testers” group so that whenever this workflow is triggered and builds a new version of my Unity Android game, all the testers in this group get an email and know they can play a new release of Slash’n’crack! 🙂
Bonus: Auto-triggering the workflow when you commit or merge
So, we’ve successfully implemented a step for Firebase App Distribution to make sure our trusted testers get the new releases of our Unity app we’re building with Codemagic.
Just one little note: For now, there is still one manual step in our entire CI/CD process — we need to go to the Codemagic dashboard to trigger a new build. But you can also trigger it automatically — right after you commit new changes to the repo.
Whether to have full automation/control of your pipeline is, in my opinion, a matter of personal preference. I’m personally always a bit wary of Git events that trigger pipelines as soon as you commit or merge some changes — from my personal experience, it can quickly clutter your intermediary tools with lots of temporary or unfinished versions…
But if you’re interested in learning more about build triggering with Codemagic and how you can run the entire building and publishing CI/CD workflow immediately once you add a new commit to your Git repo, don’t hesitate to check out the Codemagic docs. 🙂
Conclusion
By combining the Unity game engine with powerful tools like Codemagic and Firebase, we can pretty quickly set up an automated CI/CD workflow to build and publish our Unity Android apps and share them with fellow developers or testers (or investors, or whomever you think needs to see them). Now, as soon as I trigger a new build of my Unity project from the Codemagic dashboard, I know that all my Happy Testers will get an email with the updated version in the following 30 minutes. 🙂
There are, of course, lots of tools we could add to this project to really benefit from this Firebase integration — for example, it could be really interesting to add some analytics so that we gather info when the testers play with the app! And… this is something I plan on covering in the upcoming blog posts!
I hope you enjoyed this tutorial — and, of course, don’t hesitate to share your ideas for other DevOps topics you’d like me to make Unity tutorials on!
Don’t forget you can get the sample project for the entire Slash’n’crack project on GitHub over here. 🚀
Mina Pêcheux is a freelance full-stack web & game developer. She’s also passionate about computer graphics, music, data science, and other topics! She runs her own blog. Alternatively, you can find her on Twitter.
Discussion about this post