Programming

Programming is easy and useful. We believe you can learn it too. It will allow you to transform your ideas into physical realities. It can look a bit weird at first but it is actually really magical! To make things simple, it allows you to express complex behavior like logic, variables and loops by using simple building blocks.

This guide will help you to get started. Remember, everything is "logic" and that soon you will create cool things. You got this! You do not need to know everything to begin the fun, only pick what you need and everything will be okay! 👌

Something to keep in mind when taking on this new adventure, is that programming is like learning new magic tricks: you can always learn new things that will improve your project. Do not be afraid to look at other people's code, they hold some great treasure sometimes..

If you have any issue, google is your best friend or ask for help. In any case the Hologram community will always be here for you, we will be happy to help. Feel free to ask anything!

You will make mistakes, but it is part of the process! Fail forward! Mistakes are how you get good at programming - code, find the bugs, solve the bugs, continue. You'll be surprised at how quickly you get better at it. Plus, you are about to code 3D/VR.. how cool is that! 😎


Get started

CoffeeScript

Hologram is built on top of CoffeeScript. CoffeeScript is a simplified version of JavaScript that makes it way easier to code with. It also looks far better.

We chose CoffeeScript for Hologram because it makes programming accessible, easy, fun and blazing fast. Once you know CoffeeScript you could easily jump to another language without any trouble. It would look familiar. One key difference in the CoffeeScript syntax is that it uses whitespace (tabs and spaces) for its language structure, as opposed to curly brackets. This makes code generally more readable, but can also cause errors in your code.

Whitespace

Whitespace (tabs and spaces) is an important part of CoffeeScript. Though convenient to use, it makes you more prone to making errors, most of which you may miss (although we lightly visualize them in your text editor). So try not to mix up tabs and spaces.

Capitalization

Programming languages are case sensitive this means if you have a variable named box, it is not the same as Box.

Inspecting

To quickly see what is inside a variable, you can print it. This is a good way to see exactly is happening during the code execution.

print "Yo" # Prints Yo
print 41 + 1 # Prints 42

Comments

Comments can be used to explain your code, and to make it more readable. To create a comment start a line with #.

# Hahaha
myNumber = 22

# Easy right?

Variables

Variables are a useful way to store any kind of information. You can named them the way you want it! Well.. as long as it makes sense when you read it.. 😂 This of variables like little boxes containing something inside.

something = "I am .. something"

Be creative with your variables's name (as long as it’s a single word).

niceName = "a cool name"
codingIsAwesome = 12345 # A variable can be a number too

Now if we zoom out a bit and focus back on Hologram, you have to know about Entities. You will learn more about them in the guide but for now remember that they are the building blocks for pretty much everything in Hologram. Let's create one with the new keyword and store it in a variable.

myEntity = new Entity

Note that an entity is basically an empty 3D object.. so don't expect anything fancy just yet.


Numbers

Most of the time when working with values, you will use numbers to define position, rotation, scale, opacity and much more. Numbers can also be used for calculations.

Lots entity properties are expressed as numbers. Keep in mind that we are now in 3D, pixel don't really exists anymore we use meters instead of pixels.

myEntity = new Entity
  x: 10
  opacity: 0.5

You can use all the classical maths symbols with numbers like +, -, *, /. These are called operators, but you don't have to remember that.

print (20 + 100 / 10) * 6

Let's apply this to entity properties.

myEntity.x = 400 / 2

You can even combine numbers and properties in calculations. Say you want the entity x position to be half the x position of another entity.

myEntity.x = myEntityB.x / 2

Even better, you can change a variable based on itself! This can be useful if you need to offset a entity slightly, for instance moving it to the right in the example below, by adding 10 to its original value.

# myEntity.x is 0 by default
myEntity.x = myEntity.x + 10

Strings

A string is simply a text wrapped within quotation marks. In Hologram, they are often used to define color values or assets name. They can hold a singles words or even an entire book.

myBox = new Box
  color: "#FFFC00"

You can add strings together like numbers with +. In the case of a string, we call say that the text is concatenated.

# This:
print "all" + "good"

# Is the same as that:
print "all good"

On interesting thing to do is to combine variables and strings together.

name = "Steve"
place = "Paris"

print "Yo! I am " + name + " and I live in " + place
# Yo! I am Steve and I live in Paris

Cool right? Yeah but you can make this more readable using templates.

print "Yo! I am #{name} and I live in #{place}"
# Yo! I am Steve and I live in Paris

Even better, you can even add simple logic to templates.

love = 42

print "I will love you even in #{42 * 1000} years from now."

Booleans

Booleans are like light switches. Their values can only be true/yes or false/no. They are often used to enable or disable properties.

For example, you can hide a entity using the visible property.

myEntity.visible = no

Logic

The key ingredient when using code is the logic. It allows you to run a chunk of code when a certain condition is met. We call them conditionals.

Imagine that you want to toggle a box visibility when looking at another box. Using an if-statement, you can determine if the box is visible or not.

myBoxButton = new Box
  x: 2

myCoolBox = new Box

myBoxButton.onFusing ->
  if myCoolBox.visible
    myCoolBox.visible = no
  else
    myCoolBox.visible = yes

In addition to check for properties, you also have the ability to check for variables by comparing them. For instance checking if a number is smaller than or larger than another one. Now, in this next example myBox will turn blue only if you click it twice.

myBox = new Box
  color: Color.blue

clickCounter = 0
myBox.onClick ->
  clickCounter = clickCounter + 1
  if clickCounter > 2
    myBox.color = Color.blue

Note that if-statements are always booleans. One last thing, you can even check for multiple conditions at once, by combining them with and or or.

myBox.onClick ->
  clickCounter = clickCounter + 1
  if myBox.color is Color.blue and clickCounter > 2
    myBox.color = Color.red
  else
    myBox.color = Color.blue

Loops & Arrays

Okay, we are now arriving to a new step with loops and arrays! They basically give you new powers by letting you create and edit multiple entities at once! They are very useful and can really help you writing optimized and short code.

Let's create 3 boxes with similar properties.

boxA = new Box
  x: 1
  color: Color.blue

boxB = new Box
  x: 2
  color: Color.blue

boxC = new Box
  x: 3
  color: Color.blue

As you can see this is already taking a few lines of code and is a bit boring to look at. To make things a little more interesting, we can use a for-loop. The first step is to create a array that contains as many numbers as we need entities/boxes. To keep things simple we can use a syntax like this one - [1..3], which is a shorthand for an array like [1, 2, 3]. Try to change 3 by a bigger number and you will generate more boxes!

for stuff in [1..3]
  box = new Box
    x: stuff
    color: Color.blue

The stuff variable (totally random name) represents the number in the array for the current iteration of the loop. Starting at 1, followed by 2, and eventually 3. Note that stuff is actually a number! This means you can use it in your code to do any calculations. For instance we will use it here to get the position of our entities so that they are near each others on the x axis.

for index in [1..3]
  box = new Box
    x: index * 1.5
    color: Color.blue

If you run this code, you will create 3 boxes at x position 1.5, 3 and 4.5. The big picture to keep in mind is that you can add anything in arrays and use them in for-loops to get the things inside it. Furthermore, you can even add entities to your arrays and as you can imagine using a loop over them allows you to change properties of multiple entities at once.

myBox = new Box
mySphere = new Sphere
myPlane = new Plane

# All the entities in one array
entities = [myBox, mySphere, myPlane]

# Loop through each entities
for entity in entities
  entity.y = 1
  entity.color = Color.yellow

Events in Loops

Okay, now what if we would like to add an event to each of our boxes to lets say change their color when we look at them ? Take a look at the example below. You will probably see nothing wrong it. However if you run the code and look at any boxes, only the last one will change!

boxA = new Box x: 1
boxB = new Box x: 2
boxC = new Box x: 3

boxes = [boxA, boxB, boxC]

for box in boxes
  box.onFusing ->
    box.color = Color.yellow

Why? Well do not worry, it is actually very logical and we call this Scoping which you will explore a little bit deeper soon. Because the variable box is replace by the next box from our array every time the loop runs only the last execution does not destroy the variable box and can therefore be accessed again. Now to get back to our code, simply replace box in the event function by this and it will work perfectly!

for box in boxes
  box.onFusing ->
    this.color = Color.yellow # this = clicked box

Functions

A function is a block of code designed to perform a particular task. Why functions? You can reuse the same code! Define the code once, and use it many times with different arguments, to produce different results.They are most often used in Events. A function is defined by parentheses may include parameter names separated by commas (parameter1, parameter2, ...) followed by an arrow -> that signify the function keyword. Let's try to create a simple function and execute it a few times.

sayYo = ->
  print "Yo!"
  print "I like you!"

sayYo()
sayYo()
sayYo()

As you can see the lines inside the function are indented. This is called the body of the function. To make things a little bit more interesting we can tell Hologram to execute a function for us. This can be on click, fusing or any other events.

myBox = new Box

myBox.onClick ->
  myBox.rotationY = myBox.rotationY + 10

Note that this function has no name like the sayYo function above but you could also give the event a function with a name and it would work the same.

myBox = new Box

rotate = ->
  myBox.rotationY = myBox.rotationY + 10

myBox.onClick(rotate)

Now, functions can have multiple inputs and an output. Let's say you wish to convert y = x * 10 to a function.

y = (x) ->
  return x * 10

print y(42) # Prints 420

You could change the name x by anything really.

timesTen = (someNumber) ->
  return someNumber * 10

print timesTen(42) # Prints 420

The output of a function is called the return value, hence the return keyword. Let's try to rotate our boxes when we look at them using a function.

myBoxA = new Box

myBoxB = new Box
  x: 2

rotate = (box) ->
  box.rotationY = box.rotationY + 10

# Watch this! You can use the function in different places!

myBoxA.onFusing ->
  rotate(myBoxA)

myBoxB.onFusing ->
  rotate(myBoxB)

Note that multiple parameters must be separated by commas. We could add another parameter to change amount of the rotation.

# Multiple parameters
rotate = (entity, degrees) ->
  entity.rotationY = entity.rotationY + degrees

myBoxA.onClick ->
  rotate(myBoxA, 10)

One last thing, you can specify a default value for a parameter to make it optional. At this point you could omit the given value when calling the function - in this case the amount of rotation would be optional.

rotate = (entity, degrees = 10) ->
  entity.rotationY = entity.rotationY + degrees

# 10 degrees
rotate(myBoxA)

# 50 degrees
rotate(myBoxB, 50)

Objects

Objects have an important role to play in Hologram. You will find them almost everywhere from entities to animations.

They are like arrays but with a customizable key/index. Basically every key contains a value. Similar to functions they have body and therefore use whitespaces.

person =
    name: "John Lennon"
    age: 40

print person.name
# "John Lennon"

You can use objects for lots of different scenario. The most common one in Hologram is to provide options when creating a new Entity.

myBox = new Box
  x: -1
  y: 2
  z: -10
  color: Color.red

.. or add an animation to the entity..

myBox.animate
  rotationY: 10
  time: 0.5
  repeat: 10

Even better, you can loop over objects as well. Although this time you have to use a for...of loop instead of for...in for arrays to get both the key and value.

people =
  john: 24
  vincent: 23
  steve: 22

for key, value of people
  print key, value

# Or with better names
for name, age of people
  print name, age

Classes

Alright congrats for making it this far! Almost done.. well not just yet. Classes allow you to extend certain part of Hologram for your need to maybe add of change a behavior. For instance, Box, Sphere, Sky are all classes that extend Entity. They are called subclasses.

Every subclasses inherit basic functionality, however they all have extra features, like a radius for Sphere.

We can run a class by using the new keyword. This will call the special constructor function and will return an instance of that class. In this simple example, myBox is a variable that contains an instance of Box.

myBox = new Box

Okay! Now let the fun begins! You can create you own shapes or interaction in your project by using your own class that has its own constructor which specifies whatever special shapes or interaction you want. Let's create our own subclass to create a wall!

# Create Class
class Wall extends Box
  constructor: (options) ->

    # Get default box functionality
    super

    # Set special properties
    @z = 10
    @scaleY = 3
    @scaleX = 5
    @src = "wallTexture"


# Create wall
myWall = new Wall

Congrats you just created a wall!

The @ symbol refers to the Wall itself. Learn more about it here.

We can also add our own functions to the Wall.

class Wall extends Layer
  constructor: (options) ->
    @z = 10
    @scaleY = 3
    @scaleX = 5
    @src = "wallTexture"

    # Change the aspect of the wall
    @rockyWall()

  rockyWall: ->
    @src = "wallRockyTexture"

  roomWall: ->
    @src = "wallTexture"

Now, these functions are a part of every wall meaning we can use them to add interactivity to our wall, for example.

# Switch back to the normal texture when looking at the wall.
button.onFusing ->
  @roomWall()

Nonetheless, you can add events inside the class itself to get a custom interactivity. All walls now inherit this interactivity.

class Wall extends Layer
  constructor: (options) ->
    @z = 10
    @scaleY = 3
    @scaleX = 5
    @src = "wallTexture"

    # Change the aspect of the wall
    @rockyWall()

    # Add event handler
    @onClick ->
      @roomWall()

  rockyWall: ->
    @src = "wallRockyTexture"

  roomWall: ->
    @src = "wallTexture"

Comparing Loops, Objects and Classes

Programming is free thinking. Many things can be done from different ways but look the same at the end. It is really up to you to determine the best option to solve what you need to build.

For instance, you can create two blue box in a loop.

for i in [1..2]
  new Box
    color: "#FFFC00"

.. or using a function..

createBox = (color) ->
  new Box
    color: color

createBox("blue")
createBox("red")

.. or using a class..

class BlueBox extends Box
  constructor: (options) ->
    super
    @color = "blue"

new BlueBox
new BlueBox

There is no “right” way, only your own way! If you feel like some parts of your code could be improved over time, it's okay to go back and rewrite a few lines or .. everything.


Scope

Last chapter and you are free to create! The scope is simply the value of a variable within a block of code like a function. As we saw before, you may find yourself in a situation where you want to add an event inside a for-loop. Let's get back to the example from before.

for i in [0...3]
  box = new Box
    color: "blue"
    y: i * 2

  box.onFusing ->
    box.color = "red"

As you can see this code, looks fine. But if you run it and try to look at any box only the third one turns red. Hum.. why? The onFusing function uses the variable box to change the color. However, this variable is updated each time at the start of the loop.

The color only changes after you look at the box, and at that moment the value of box is what it was set to the last time the loop ran: the third box.

Using "this"

Instead of using the variable box we can also find the box that was looked at by using this. The value of this depends on where it’s used. Within event handlers, it will refers to the entity, in our case the clicked box.

for i in [0...3]
  box = new Box
    color: "blue"
    y: i * 2

  box.onFusing ->
    this.color = "red"

You can also use @ as a shorthand for the this keyword, here using the click event.

box.onClick ->
  @color = "red"