The Anatomy of a Haxe Project

Before divulging all the dirty little secrets of my latest project, I thought it would be nice to have a little chat about my Haxe workflow as of late. Starting with if(adventure), moving into ARventure, and now my latest endeavor, I've been developing and refining how I set up my projects. I've had to adopt more than a few quirks that I figure would be wise to document and discuss.

Let's start from square one. When I worked on if(adventure), it was the first time I had worked with Haxe targeting only Javascript/HTML5 as a platform. When working with a multi-platform engine like HaxeFlixel, the build process does most of the heavy lifting as far as generating a webpage and whatnot to target HTML5. Since I didn't use an engine for if(adventure), I had to take care of that part myself. I ended up with what was essentially a second project nested within if(adventure), which contained the HTML and CSS for the webpage, while the build process for the Haxe source generated the Javascript. It's worth mentioning that this process was brand new to me and the project was created in 48 hours for a Ludum Dare, so there wasn't much time to figure it all out. I used Brackets for my code editor, but was unable to get the Haxe extension to function, so I had no code completion. Meanwhile, I was building through the command line, with a little text file that I could copy and paste commands from for different build settings. All in all, it was a rough first attempt, but laid down a pretty good foundation.

When I started up ARventure a few months later, I started improving my workflow drastically. First and foremost, I chose Visual Studio Code as my editor and actually got the Haxe extension to work. Hooray for code completion! I also set up an HXML build file for my project that allowed me to include my build settings, libraries, and export file path all in one neat little package. Then to top it all off, I setup keyboard shortcuts in VS Code to run that build file for me. Awesome. ARventure also introduced some new complexity into the project's workflow compared to if(adventure). Namely, I was using the Phaser game engine and there was actual art and other assets that needed to be included. Phaser obviously isn't a Haxe engine, so I had to use an extern to work with it, while the actual Phaser source code sat in my build directory along with all my generated Javascript. Nothing too crazy, but it did pave the way towards my workflow today.

Now we'll get into the real meat and potatoes of my Haxe-to-JS workflow. Up to this point, we've talked about the nested HTML project, including art assets and external JS libraries, and setting up a working editor. As my new project contains a client- and server-side component, however, a new wrinkle was added to the mix... Node.js. Conceivably, working with Node in Haxe isn't all that different that working with Phaser. In fact, the externs for Node were created by the Haxe Foundation themselves. However, Node requires its own project structure, which in turn created a second nested project as well as extra workflow considerations.

I figure the easiest way to explain the workflow is to go through my directory structure from top to bottom, so let's break it down:

  • At the tippy-top of my project is my development and production directories. Development is where I keep the actual project files, while production is where I stow away notes, reference images, and working files for art. Production is sort of a mad house when it comes to organization and is ultimately left out of the final project, so we won't go further there.
  • Inside the development directory are the source and export directories. The source directory is where the Haxe source code is held, which in turn is split up between client, server, and shared files. The export directory holds the nested projects for the HTML and Node.js builds. Additionally the build HXML's are also in the development directory. There are 3 HXML's, one to build the client, one for the server, and one that simply runs them both.
  • Diving into the export directory, there are two more directories inside, one for the client and one for the server.
    • Inside the client directory is the HTML project, with an index.html, the Haxe-generated JS file, as well as any libraries, CSS, and asset files (organized accordingly, of course). 
    • Inside the server directory is naturally the Node.js project, with its own generated JS, package.json file, and node_modules directory. Similarly to how I have to include native JS libraries to the client project, I also have to install any Node modules I want to use through NPM.

It took me a little while to fully wrap my head around how I needed to structure my project for this workflow. Initially I wondered how the hell I was supposed to include modules in my project, before realizing that I was literally creating a Node project with Haxe-generated JS. I simply had to install modules through NPM and have them waiting for me when I build the Haxe code to JS. Then I wondered how the hell I was supposed to access the modules I installed through NPM, before remembering that I would need a Haxe extern (or a whole lot of untyped JS in the Haxe source, but that's a different sort of discussion). There were definitely some speed bumps, but now I've started cruising and it's awesome.

I will fully admit, this workflow is a little convoluted. It would make much more practical sense to simply work with JS when web is my only target platform. But Haxe takes away all the headaches working with JS usually brings. Beyond that, the beauty of Haxe is its portability. One of the worst parts of jumping between different languages in school was that if I wanted to reuse some old code, I usually had to port it from something silly (like Processing or Greenfoot) into whatever I was currently working on. There are a handful of functions that I've probably ported 4 or 5 times each. So, yes, my target platform is currently web, but in the future I will be able to recycle any code I write for any of the other platforms Haxe supports. That sort of flexibility is exactly why I've chosen Haxe as my primary development language.

Now if only I could find a cross-platform Haxe game engine I enjoy using... But that's an issue for another day.