Looking into Scala.js
This blog post was created to see how Scala.js is holding on and having some fun with programming. I decided to do a simple mini-game using Scala.js and Three.js libraries.
I will explore Scala.js from a practical perspective. You’ll see the perks of using typed languages in frontend. We are also going to take a look under the hood and see some generated JS code. Finally, I’m going to present some advantages and disadvantages of Scala.js and try to answer the question: Is Scala.js production ready?
I encourage you to read this blog post about Scala.js basics first, but it’s not required.
My inspiration for the game was: https://oos.moxiecode.com/js_webgl/xwing/
Source code of my game: https://github.com/andrjew/scalac-fighter/tree/0.1.1
Why Three.js library? I chose it because I’m familiar with it and I knew that it’ll provide me things I need. Three.js is in fact 3D library and initially I also wanted to create a 3D mini game, but it ended up being 2D ;)
This is how the game looks like:
And here you can have some fun and play: https://fighter.scalac.io
I know the game is far from perfection. I’ve stopped developing it when the game became playable. That leave much place for improvements. If you want to add some features to the game or just see how it is to program in Scala.js, just grab the code from Github: https://github.com/andrjew/scalac-fighter.
About the project
We had our fun time, now let’s see how the game looks from the inside.
Firstly please see project structure to understand basic concept:
I believe architecture here is not too hard. Every class is quite self-explanatory. The most of game’s logic is located inside Game class.
One thing, which I want to notice is the force of having typed language like Scala. The advantage over JS is that I’m unable to accidentally confuse
Moreover, please look at this example from Game.scala:
This code is responsible for moving objects on each animation frame. But I only want to move object that are self-moving (like bullet), so I created a special SelfMoving trait. As a result I can use pattern matching on game’s objects collection and easily move only self-moving items.
The Type System Strikes Back
Another example will be from Main.scala:
This piece’s task is to react on player’s mouse movements. For capturing mouse movements I’ve used the jQuery library (wrapper). We can see that Scala.js allows us to use anonymous function just like in Scala.
But something more intriguing is happening here. Our anonymous function take parameter of type
JQueryEventObject and unfortunately this type doesn’t have
event.clientX field, which I need to move player’s ship. This happens because jQuery event is normalized into jQuery abstraction (see this for details). The funny thing is that the field
clientX is there (we can observe it while debugging JS), but we can’t see it, because we’re limited with
Sadly attempt of using partial function:
won’t succeed, there is an compile error:
The argument types of an anonymous function must be fully known. (SLS 8.5).
Neither below mixing works:
The parameter type must be exactly
JQueryEventObject class or it’s ancestor.
And not surprisingly pattern matching also fails:
This fails because proposed case will never be matched.
All of our Scala forces has failed. We need to use the dark side of the Force:
Solution is to cast
event: JQueryEventObject object to DOM’s standard
MouseEvent which is represented in Scala.js under
org.scalajs.dom.MouseEvent class. This works because in JS runtime this object is in a fact an
Maybe some persons sense some more disturbance here. If yes, then you’re right. The dark side reveals again:
One of the most terrifying power of the dark side, the mutability attacks!
The mutable trap
Please see following example from GameScene class:
As you can observe this class is not written in immutable style. Adding a new object to GameScene doesn’t create new instance of itself. Instead of that it is changing it’s inner state. Yes, I know. We don’t like mutability, but there is a cause of this state.
Please notice that
scene is instance of
scene.add(gameObject.object3d) as it was pure JS code, which you can’t change. Unfortunately JS implementation of function
scene.add(...) is mutable and this is something I had to accept.
Three.js library was designed in mutable way and this is not a mistake or bad thing. The problem occurs when we want to write our application in immutable/functional way as we used to do in Scala.
Unfortunately when using a Three.js under the hood we are not able to achieve this easily.
It’s possible to fight JS libraries mutability, but when taking Scala.js (in immutable style) into consideration always remember about possible extra work overhead and performance issues.
Where is JS ?
sbt fastOptJS command, you can find output files in
scalac-fighter/target/scala-2.11 directory. The most important file there is
scalac-fighter-fastopt.js. This file includes compiled game code and also Scala.js related code. That’s making the file 17 783 lines long :(
This is unnecessary obstacle and makes reading compiled code even harder. I’m aware that not every person will explore gendered code, but some compiler option providing my code and Scala.js separately would be great.
Let’s take a quick look into some example:
As we can see the generated code is not very eye-friendly. Function name in JS is 5 times longer than in Scala.
__s_Option fragment is responsible for noticing scala’s
Optionhere, but this is only my hunch.
org_denigma_threejs_Vector3 is just fully qualified class name for Three.js object.
Quite mysterious function is
$uD, but we can find it in generated code and this is:
So mystery is solved ;] this is just a function that ensure Double type. Further we see how Scala.js compiler handle Scala’s
Option by using simple
Should I care about generated JS ?
Generally I think not, but it’s good to know what’s going on ;]
Even tough reading compiled Scala.js code is hard, looking into it will make You better understand how Scala.js works and what limitations it could have.
Things I like:
- Easy to start working with
- Many, ready to use JS libraries facades for Scala.js. I was very positively surprised to find a Three.js facade
- Good alternative for person who must do something in JS, but can’t look at it :)
- You can debug Scala code directly from browser. This really works and it’s great! This is possible thanks to sourcemap mechanism. To try this just open developer tools in browser and then in source section debug Scala code like you normally debug JS code.
- Of course type system (safety, clarity and other)
- Support for autocomplete and other perks of IDE
Things I dislike:
- You can’t escape from mutable JS libraries
- Generated code is rather hard to read (but not impossible)
- Possible more inconveniences like the one with
Can I use Scala.js in production ?
Probably this is the most important question. Is it production ready ?
Unfortunately I’m not going to give you an easy answer. More proper question is: Does Scala.js do the things that I require? Does it complete my requirements ?.
In summary my answer won’t be just Yes or No. Instead of that I will provide a checklist, that will help you to know if Scala.js is something you can use.
Checklist (before using in production):
- Absolutely MUST do a recognition by yourself.
- Learn how Scala.js works, what are the differences between Scala and Scala.js.
- Read docs(Scala.js pages) and other articles(Hands-onScala.js)
- Try Scala.js in applications / projects:
– write some mini project
– try to break/hack Scala.js
– take my project and just have some fun with it ;)
- Look into generated JS code to be aware what’s really going on.
- Scala.js team. I want to say that I admire you for work that they already done. Combining these two languages is not easy for sure. Keep fighting the good fight.
- Three.js team – I’m enthusiast of this library for quite long time.
- Creator of facade for Three.js library: https://github.com/antonkulaga/threejs-facade.
- Patryk Jażdżewski for help with project and game code.
May the force be with you :)
Do you like this post? Want to stay updated? Follow us on Twitter or subscribe to our Feed.