SoundManager


using UnityEngine;
using System.Collections;

// This is a SoundManager. Everything audio in the project will be managed through here.
// Clips can be played calling the appropriate functions in the code and assigning the audio clips in the Unity Editor.
// For info on the various functions available please check the comments in this file or look in the documentation.

public class SoundManager_01 : MonoBehaviour {

	public AudioSource sfxSound;				// Sound source for SFX
	public AudioSource msxSound;				// Sound source for Music
	public SoundManager_01 instance = null;		// instance of sound manager value, set to null

// In the Awake function we check for multiple instances of the SoundManager.
// For performance optimization and to avoid conflicts we will destroy all the SoundManager objects that are not this.

	void Awake (){
		// check if there are no instances of the sound manager
		if (instance == null)
			// in this case set it to this
			instance = this;

		// if there is already an instance of the sound manager, but it's not set to this
		else if (instance != this)
			// destroy it.
			Destroy (gameObject);
	}

// From here on we will define the different functions needed...
	
// The first function we need is the play function for a single sound.
	public void PlaySingleSound (AudioClip clip){
		// assign the clip passed in to the sfx sound source
		sfxSound.clip = clip;

		// and play it.
		sfxSound.Play();
	}

// The second function plays a random sound from an array of sounds, randomized for pitch and volume
	public void PlayRandomSound (float minPitch, float maxPitch, float minVolume, float maxVolume, params AudioClip[] clips){

		// generating a random number to use as index selector for play
		int randomNumber = Random.Range (0, clips.Length);

		// assign the clip to our sfx source
		sfxSound.clip = clips[randomNumber];

		// support for random pitch
		AudioPitchRandomizer (sfxSound, minPitch, maxPitch);
			
		// support for random volume
		AudioVolumeRandomizer (sfxSound, minVolume, maxVolume);

		// and play it
		sfxSound.Play ();
	}

// Function for pitch randomization functionalities
	private float AudioPitchRandomizer (AudioSource sfxSource, float minPitch, float maxPitch){

		// generating a random multiplier for pitch, in the [minPitch, maxPitch] range.
		float randomPitch = Random.Range (minPitch, maxPitch);

		// scaling the pitch of the sound source by its multiplier
		sfxSource.pitch *= randomPitch;

		// return it to the caller
		return sfxSource.pitch;
	}

// Function for volume randomization functionalities
	private float AudioVolumeRandomizer (AudioSource sfxSource, float minVol, float maxVol){

		// generating a random multiplier for volume, in the [minVol, maxVol] range.
		float randomVolume = Random.Range (minVol, maxVol);

		// scaling the volume of the sound source by its multiplier
		sfxSource.volume *= randomVolume;

		// return it to the caller
		return sfxSource.volume;
	}
}


Case study: side-scrollers (Part 1)

As perhaps most of you already noticed, side-scrollers made their way back into the gaming scene. Most of this success is due to an increase in releases of endless side-scrollers on mobile and tablet platforms. So being one of those kids that spent hours and hours (and tons of coins) in classical side-scrolling arcades I felt the need of offering an analysis of their audio. The first part of this study will focus on sounds and how a typical session for a SSG (short for Side-Scroller Game) may look like.

Back to basics

Speaking of sounds, SSG are the easiest games for what concerns implementation. There are usually few sounds and their only purpose is to communicate to the player a specific action which can be an attack, a jump, getting a coin and so on. It is almost never the case that sounds need to portray a feeling such as anger, sadness, love, etc. etc. Very rarely, in the end, ambience sounds are present. This leads to a fairly light and simple implementation session. The same cannot be said for the design of such sounds. Being the only element of communication to the player of the actions it is quite important that these sounds are carefully crafted. Many of us still remember, roughly 30 years later, Nintendo’s Super Mario Bros and its sounds: coins, pipes, jumps and so on. There are of course modern exceptions (i.e. Little big planet, by Media Molecule) where there are evolving ambience sounds, 3D objects positioning, physics interaction sounds with velocity layers, and the sonic characteristics of the sound sets the overall mood of the scene.

Quantum mechanics

Quantum mechanics, aka the building blocks. Let’s examine together what are the smallest elements that constitute a SSG. We can divide them in 3 main categories:

  1. GUI sounds
  2. Cutscenes sounds
  3. In-game sounds

GUI Sounds

GUI is an acronym that stands for Graphic User Interface and in videogames it is used to refer mainly to the menus (main, pause, inventories, …), but also to on-screen elements that inform the player of their current status, health, magic points, bullets, and so on. Very rarely though these latter elements have sounds associated with them, so from here on when we’ll talk about GUI sounds we’ll refer to menu sounds. After this brief introduction let’s delve deep into what are the sounds that make up the GUI sound environment:

  • move selection arrow
  • select
  • go back
  • pause the game
  • resume the game
  • start the game (optional, otherwise can be the same as “select”)

That’s it. Super duper easy.

Cutscene Sounds

Some SSG can include cutscenes, either as an introductory clip to set the background of the game, or as a device to develop the story further. Cutscenes are very much alike all cutscenes from the other games, but with the limitations of the game considered. So if no footsteps or physical interactions are heard throughout the game, then the same applies for the cutscene. Sometimes ambiences and other sounds relevant to the narration, including dialogues, may be included though (see Castlevania: Symphony of the night).

In-game Sounds

Last but not least the in-game sounds. These are, depending on the kind of SSG, generally:

  • Player sounds:
    • Moves (walks, runs, sneaks…)
    • Jumps
    • Attacks
    • Dodges
    • Blocks
    • Gets Hit
    • Dies
  • Weapon sounds:
    • Hits
    • Misses
    • Gets blocked
    • Blocks
  • Enemies sounds:
    • Attacks
    • Gets hit
    • Dies
  • Other sounds:
    • Miscellaneous objects interactions
    • Miscellaneous objects movement

As already mentioned, in this case, the care in the design is what sets apart a great game from an okay one. All the above sounds must be designed so that the player can really connect to the game and wants to play again, or wants to perform again an action. An example of this technique is the reward feeling that one gets by grabbing coins in Super Mario Bros. or the sense of empowerment in Metal Slug when you get the “Heavy machine gun” weapon.

The importance of this design consideration becomes very clear if we consider some recent Endless-Scroller Games (ESG) available on mobile platform that feature monetization systems. The rewarding/fulfilling feeling a player gets, not in performing the transaction itself, but by playing the game (or sub-game) will lead the player to complete a transaction. Of course it is a Game Designer duty to make the system enjoyable and drive a player to actually make the transactions. That said, it is our duty as Audio Designers not to disrupt the Game Designer vision and to help him/her in conveying that idea in the absolute best way possible, as the monetization system is incredibly important for a mobile game.

Final thoughts

So as you can see we are talking about a very small number of elements here, ranging from 20 for a simple game to 40-50 for a more complex one. This makes the session very manageable, but also the design difficult. Even though the sonic quality of the audio has improved dramatically on SSG the same cannot be said for the variety in sounds. in fact due to size limitations of the hosting platform memory the sounds are still limited to 2-3 variations per sound in the best cases. This makes it hard because the designer will have to create a sound that is engaging when heard for the first time, and that won’t sound annoying when repeated over and over. Even though this may look like a huge limitation I think that it is actually one of the most characteristic traits of SSG audio and what makes SSGs immediately recognisable as such.

In the next part we’ll deal with interactive music, its function and a dynamic music implementation session.

Pausing Sounds in Javascript

Hello amazing reader!
Today I want to discuss something that I had to address while working on AngryBots. If you haven’t played the game yet you can download my version here.
If you have played the game you may have noticed that when you’re hacking a terminal, if you step away from the computer the progress bar animation pauses. If we simply set a sound to play when the hacking starts, then we will missing out the fantastic opportunity to make pictures and sounds work together. In fact the sound will just playback until the end while the bar will pause. To avoid this and to take full advantage of the auditory experience that we can offer to the player I decided to modify the original script called TerminalHack.js. Here’s how it goes:


#pragma strict
@script RequireComponent (Health)

Ok, so this part just above is the header. If you remember, from my previous post, the header is where we tell the cook where to look for the ingredients. So it’s either the fridge, the cupboard, or in this case in another script called Health.js
Actually, in Javascript, we have this nice feature which is called “pragma”. I don’t want to get too technical, but what #pragma strict basically does is to tell the compiler to warn us if we write “bad code”. Mind, not “wrong” code, just “bad”. It’s like your significant other saying: “You’re not going out dressed like that, are you?!”.

Then we can start to declare our variables. What we need is 5 simple things: a name for the Wwise event that pauses the sounds, a name for the Wwise event that resumes the sounds, a guy raising a flag if the hacking as started, a girl raising a flag if the hacking is in progress and finally their parents that tells them to go back home cause it’s late and the hacking is completed anyway. The first two are gonna be strings, the last three are gonna be boolean.


public var hackStartEventName : String;
public var hackCompleteEventName : String;

/************************** ADDITIONAL AUDIO CODE *****************************/
public var hackPauseEventName : String; // when OnTriggerExit is called the hacking pauses
public var hackResumeEventName : String; // when the hacking is paused it resumes
private var hackingStarted : boolean; // tells if the hacking has started so we can pause the sounds and resume them
private var hackingInProgress : boolean; // lets us determine wether the hacking is in progress or not
private var hackingCompleted : boolean; // tells if the hacking has been completed
/************************** END OF ADDITIONAL AUDIO CODE *****************************/

/************************** ORIGINAL CODE *****************************/
private var health : Health;
private var animationComp : Animation;
// initialising the two variables just defined
health = GetComponent. ();
animationComp = GetComponentInChildren. ();

Ok, so now that all the variables we need are there we will go on and initialize them as soon as the game starts. Obviously when the game starts the hacking won’t be already started, and it won’t even be in progress or completed, so we’ll set everything to false. On a side note, the two string variables we defined (the ones holding the name of Wwise events) will be available to set and/or modify in the Unity editor, as we declared them as public variables. We’ll therefore set their initialisation value there.


function Start () {
	UpdateHackingProgress ();
	enabled = false;

/************************** AUDIO CODE *****************************/
	hackingStarted = false; // hacking has not started
	hackingInProgress = false; // is not in progress
	hackingCompleted = false; // and has not been terminated
/************************** END OF AUDIO CODE *****************************/
}

Ok, so the way this terminal hacking thing works is through the use of a collider and a health systems. It’s like if the player deals damage to the computer while standing inside of the collider. When the health reaches zero a signal is sent that announces that the hacking operation was successful. Here’s how it works when the player is inside:


function OnTriggerStay (other : Collider) {
	if (other.gameObject.tag == "Player")
		health.OnDamage (Time.deltaTime, Vector3.zero);
}

Now, we’re not really interested in what happens when the player stands in front of the terminal. We are more interested in what happens when the player gets in and out of the collider area, i.e. the area in which the player deals damage to the terminal. Here’s what happens when we enter the area:


/************************** AUDIO CODE *****************************/
function OnTriggerEnter (other : Collider) {
// if the game object entering is the player AND the hacking has not started yet
	if (other.gameObject.tag == "Player" &&  !hackingStarted) {
		// then start the hacking process and call the starting event in Wwise
		AkSoundEngine.PostEvent(hackStartEventName, gameObject);

		// set hackingStarted to true because the hacking process has started
		hackingStarted = true;
	}

// if the game object entering is the player AND the hacking percentage is non-zero BUT the hacking has previously started
	else if (other.gameObject.tag == "Player" && health.health && hackingStarted){
		// then resume the event that was stopped by calling the resume event in Wwise
		AkSoundEngine.PostEvent(hackResumeEventName, gameObject);
	}
}
/************************** END OF AUDIO CODE *****************************/

So, even if the code is commented quite thoroughly a little explanation won’t hurt here. Basically we can find ourselves in two different situations when entering the collider. First case is that the hacking has not started yet, and what will happen here is that we will call the event to start the hacking sound. The second case though is when the player already started to hack the terminal, but then, for some reason, she left the area in which she can deal damage to the terminal. In this other scenario we will have to resume an event that surely must have been previously paused. There is actually a third scenario (I know I said it was only two). And it’s when the player hacks the terminal successfully. Now, if she were to leave the area and then come back to it, the resume event would get called in Wwise, because the hacking has started. If we include a check on the health in the if statement we will avoid calling a useless event and save some resources.

Now let’s have a look at what happens when we leave the collider. Again, there will be two scenarios: the hacking is complete, the hacking is not complete. The first case is not of our concern now, as we’ll deal with it later. The second case though will have to be addressed now:


/************************** AUDIO CODE *****************************/
function OnTriggerExit(other : Collider){
// if the player is leaving the collider AND the hacking progress is not completed
	if (other.gameObject.tag == "Player" && !hackingCompleted){
		// pause the hacking sound by calling the relative pause event in Wwise
		AkSoundEngine.PostEvent (hackPauseEventName, gameObject);
	}
}
/************************** END OF AUDIO CODE *****************************/

As you can see, it works pretty similarly from the entering code we wrote before. If the player gets inside the area and the hacking is not completed, then we will pause the event.
At the end of the following bit of code we tell Wwise that the hacking is complete and we set the variable we defined earlier to be true, and this will tell all of our code that: "Yes! The player is awesome! She hacked the terminal!! Hooray!"


function OnHacking () {
	enabled = true;
	UpdateHackingProgress ();
}

function OnHackingCompleted () {
	AkSoundEngine.PostEvent(hackCompleteEventName, gameObject);
	animationComp.Stop ();
	enabled = false;


/************************** AUDIO CODE *****************************/	
	hackingCompleted = true; // the hacking process is completed, so we set the relative variable to true
/************************** END OF AUDIO CODE *****************************/	
}

And we're done! Now if the player wanders around the audio will match the picture. Here you can see the final part of the code as well as the Update function. They're not part of the audio but they are essential for the animation to work properly.


function UpdateHackingProgress () {
	animationComp.gameObject.SampleAnimation (animationComp.clip, (1 - health.health / health.maxHealth) * animationComp.clip.length);
}

function Update () {;
	UpdateHackingProgress ();
	
	if (health.health == 0 || health.health == health.maxHealth) {
		UpdateHackingProgress ();
		enabled = false;
	}
}

I hope this is helpful for you, and that you will use something like this in your games, as audio scripting/programming is what can make your already amazing audio blend seamlessly into the game and enhance the player experience.

Take care!

Audio design and C# scripting

Hello lovely chaps!

Today I want to discuss some Audio design choices through scripting in C#. I recently finished a course on video game audio production using Wwise. It was hosted by Berklee, it was awesome and you can check it out here!

One of the projects we did was AngryBots. If you haven’t played it, the game consists of hacking terminals to get doors open. At a point in the game you’ll obviously face the final door. I thought it would be a good idea at that point to emphasise the importance of the moment, and music by itself wasn’t just cutting it, as it would have been changing only after entering the final door. So I decided to tie the Final_Door_Opening event in Wwise to a custom transition for the playing music, so that it will stop but not abruptly. This left some space for the door sound (which is quite dramatic) and a nice tremolo strings pad (to build some tension). Now, that alone wasn’t enough. It was just a tremolo string pad going on with no change whatsoever and it was feeling repetitive after a bit. So the idea was to tie the volume of that tremolo strings to a RTPC (Real Time Parameter Control). RTPC are basically values that the engine (Unity3D in this case) sends to Wwise. The choice on what would have controlled the volume change obviously fell onto the distance that separates the player from the door. But there was no script to tell me how much that distance amounted to. Even worse, there was no script that would send such an information to Wwise. So I decided to get my hands dirty and do it myself, and here I’ll show the code I used and I’ll explain it bit by bit and I’ll assume you have no programming knowledge whatsoever.

So, we start with the header:


using UnityEngine;
using System.Collections;

This is code that goes before all the other code. It is important because basically it tells us (and the compiler) what are the libraries that we are going to use. Imagine you’re a cook. The header tells you were you’ll find the ingredients for your recipe. It’s like saying: “using Fridge;” or “using Cupboard.SecondShelf;”.

Then we declare the name of the class.


public class DistancePlayerDoor : MonoBehaviour {

We have a few privacy options here, but for simplicity and for easiness while dealing with Unity we’ll go with public. This means that all other classes can see and use material from this class. It’s like a library. Some rooms are locked, some aren’t. You have access to the books in the unlocked rooms, and we are basically saying that people will have access to this room. Also, in this case, this class inherits the properties from another class which is called MonoBehaviour. This basically means that our room will have the same furniture disposition as the MonoBehaviour room, and that we will move around this room just as we would in the other. This also means that if MonoBehaviour is a silent room, so will be our DistancePlayerDoor.

We can now proceed in declaring the variables that we’ll need:


private float distance;
public int MAXDIST = 30; // this is set to public to tweak within Unity3D inspector
private bool isDoorActive;
private Vector3 doorPosition;
private Vector3 playerPosition;
private FinalDoorUnlocked finalDoorUnlocked;

As you can see I set all of those variables to private but one. That public variable MAXDIST is my weighting factor and I’ll be able to tweak it from Unity to define the area in which the volume change will happen.

All the other variable names are quite self-explanatory. So distance is the distance between the player and the door, which is what we are looking for. isDoorActive tells me wether the Final Door can be opened. If you remember, to open the doors, the player has to hack some terminals. This doors requires two terminals to be hacked for it to open. So we’ll just check wether this is open or close and store the answer in this variable. doorPosition and playerPosition are the co-ordinates in the 3-dimensional space for the player and the door. We’ll need it to calculate the distance. Finally, finalDoorUnlocked is an instance of the FinalDoorUnlocked class. This basically means that we are referencing another script. In simple words (the library example) we set up a camera to see what’s going on in the room called FinalDoorUnlocked and we can see it on our smartphone through an app that we called finalDoorUnlocked. Take this Watchdogs!

Now it’s time for the Awake function.


void Awake (){
     finalDoorUnlocked = GetComponent<FinalDoorUnlocked> ();
     isDoorActive = finalDoorUnlocked.isDoorActive;
     doorPosition = GameObject.Find ("LockedDoorFromHellDouble_3").transform.localPosition;
}


The Awake function, is a bit of code that gets called whenever an object is made active for the first time. Often this is at the start of the game, but that is not always the case. In the Awake function you assign a value to the variables you defined earlier on. So it’s basically like filling up the library for the first time and deciding where to put each section: “Narrative” goes here, “User’s guides” goes there, “Comic books” over there, and so on. They can be rearranged in the future, but you’ve got to have a starting point. (Do you see the point in setting variables as private now? People won’t just take your books and put them back in the wrong place!). So, the first thing that we are doing is to take our smartphone, open our finalDoorUnlocked app and turn on the camera. This is done through the GetComponent method, which does exactly what it says. It gets a component from a gameobject. In this case our component is another script, which is called “FinalDoorUnlocked”. One of the properties of this script is isDoorActive. Guess what it tells us?! Exactly! So we store that value (which is true or false) into our variable called… isDoorActive. Beware that this second variable is relative to our script and it’s different from the other isDoorActive! It’s like copying the settings of your friend’s smartphone. Same model, same brand, same settings, different phone. The last thing we do is to find the co-ordinates of the door. As it will not be moving during the game it is alright to do it here and call it a day.

Obviously when playing a game things changes rapidly, and we want our scripts to be up to date with what’s going on in the game all the time! Lucky we have a method to do that. Guess how it’s called?! Yes, the Update method.


void Update(){
     if (isDoorActive != finalDoorUnlocked.isDoorActive) {
          isDoorActive = finalDoorUnlocked.isDoorActive;
     }
     // if the door is activated
     if (isDoorActive == true) {
          playerPosition = GameObject.FindGameObjectWithTag ("Player").transform.localPosition;
          Debug.Log ("player position is: " + playerPosition.x + playerPosition.y + playerPosition.z);
          // call player distance from door calculator
          distance = DistanceCalc (playerPosition, doorPosition);

// distance handler: puts it in the correct range if it's out if (distance > MAXDIST) { distance = 100; } else { distance = distance * 100 / MAXDIST; } // call function for rtpc "Distance_Player_FinalDoor" to Wwise AkSoundEngine.SetRTPCValue("Distance_Player_FinalDoor", distance); } }

Now this is juicy stuff!!! Woah woah woah, let’s slow down a bit and tackle this bit by bit. We’ll actually start from the end of the code to understand what’s going on. So: why are we writing this code in the first place? To send a value to Wwise under RTPC form. So let’s examine this bit of code:


AkSoundEngine.SetRTPCValue("Distance_Player_FinalDoor", distance);

This is a function that is found in a script provided by AudioKinectic (Wwise’s developers) that allow us to send a RTPC value to Wwise. As you can see it takes two arguments: the name of the RTPC as set up in Wwise, and the value that we want to pass, which in our case will be stored in the distance variable.

But the thing here is when will we need for this parameter to be passed? Do we need it always? Or just sometimes? Of course we want this to happen when the Final Door has been unlocked. And so, just on top we write the conditional statement:


if (isDoorActive == true) {

The problem that arises now is that we don’t have any value for the distance between the door and the player yet! Come to think about it, where is the player exactly?! Let’s track her down!!


playerPosition = GameObject.FindGameObjectWithTag ("Player").transform.localPosition;

This makes use of the “Tag” feature in Unity. It’s like having a handy GPS that always tells us where the player is. In this way we can then ask for the informations contained in the transform component, in particular we’d like to know the localPosition, i.e. the player co-ordinates. Ok, now that we know where the player is let’s calculate the distance. We’ll write a method that’ll do that for us, but we’ll do that later. We’ll call it DistanceCalc, and it will take in 2 arguments, namely the player position and the door position:


distance = DistanceCalc (playerPosition, doorPosition);

Great! we have now have the distance! We also know that our RTPC will have a value from 0 to 100, so what we’d like to do is to handle the distance value (which will be in game units) and make it a percentage. So obviously the closest the player can be to the door is 0. The furthest will be MAXDIST (look who’s showing up here, hello!). So, things can work out in 2 ways. The player is farther than the MAXDIST, or the player is in between MAXDIST and 0.


if (distance > MAXDIST) {
     distance = 100;
}  else {
     distance = distance * 100 / MAXDIST;
}

In the first case we just set the distance to be the maximum possible in Wwise. In the second one we use the MAXDIST value as a weighting factor to express the current distance as a percentage of the maximum distance (and make Wwise happy).

This is all great, but all of this won’t ever get executed for the time being, because for how thing stands the door will never be active. We didn’t check wether the door is open or not. Yes, I know we used the code to say that IF the door is open then execute the code, but the value of isDoorActive, will always be false! “Why is that?” Thanks for asking me. You see, in the Awake function we checked wether the door was open or not (and it surely wasn’t as we were at the beginning of the game), and we wrote down this fact on a piece of paper near us. The problem is that the IF statement in the Update method is actually checking that little piece of paper, and not the actual door. So we need to remember to check that door from time to time (60 times a second to be precise). We do this by adding another IF statement that will wrap everything together:


if (isDoorActive != finalDoorUnlocked.isDoorActive) {
     isDoorActive = finalDoorUnlocked.isDoorActive;
}

As you can see we are checking wether our isDoorActive value differs from the real one. If that is the case than things have changed and we need to update our info. So we’ll quickly scrap that “false” we wrote before and write a “true” down. This is more efficient than asking the system to re-assign the value each frame. It just checks wether things are equal or not, if they’re not then it bothers writing it down. Otherwise we’ll have tons of wasted paper.

Just brilliant! Everything is looking good and it should work! So you open Unity, click the play button, play it to the Final Door and… and Unity slaps you right in the face! “What exactly is DistanceCalc?” *slap*

Ah! We didn’t calculate the distance. But we’ll do it now, it’s alright! Let’s go:

Do we need anybody else to access this function? Well, it is quite generic, but in this case we don’t really need it. Just set it to private and call it a day.

What kind of value will this function return? Oh, it’s going to return a distance so a float value will be the best choice (a float is a number with decimals)

Will this function need any kind of input when called? Sure, it’ll need the co-ordinates of the things to calculate the distance between.


private float DistanceCalc(Vector3 playerCoordinates, Vector3 doorCoordinates){
     float result;
     Vector3 delta;
     delta = doorCoordinates - playerCoordinates;
     result = delta.magnitude;
     Debug.Log ("distance is: " + result);
     return result;
}

So here we are. result is obviously the result of the operation we are about to perform and it’s what we want our function to spit out. Just like the distance it is a float. delta is a 3-dimensional vector. Now, get your algebra book and look under the vector section. The distance between two points in space is the magnitude of the vector obtained by their difference. So delta is the vector obtained by subtracting the player coordinates from the door coordinates. It tells us how far is the player from the door and what direction in the space she is facing. We don’t need the info about the direction in space, so we’ll just write down the magnitude of the delta vector. And because that is what we were looking for, we will store it into the result variable. The result variable will then be returned (spit-out) and assigned to the distance variable in the Update method.

This is it! We are done. Now you can go to the Unity inspector and with your Wwise session hooked up you can tweak the MAXDIST value to better define the boundaries of this effect! If you want to check out how this sounds feel free to check out my showreel on the home page.

Well, I hope this has been helpful and gave you some food for thoughts! Feel free to leave a comment and let me know what you think about the post, the topic, life, the universe and everything!

Take care!

A fresh start

Hi!
This has been a rather rainy summer. And I’m quite happy about it. As you can see I am currently re-vamping my website, with a new theme and loads of super fantabulous content! I’ll be soon starting a blog, where you’ll find articles mainly about code and scripts, or technical content in general. I may also post some audio/music related material from time to time. Anyway, make sure to check it regularly! 😉
This is the start of an awesome journey, so let’s go and enjoy it!

All the best,
Lorenzo.