Here is the chronicle that I might produce during the last 10 years running my little pet project Moulinella but it’s written now by the huge help of repository’s history. I sincerely tried to describe how the project (especially, its dependencies) has been evolving and why. How new features, issues and external changes influenced it.
At the end of the article, you can find the current state (platforms, services, languages, libraries) and the list of items that were used but passed away. So you can scroll to the end, scan the list to decide if it worth to read the whole article or just to find the parts you really interested in.
I had some free time and my wife used to have fun cross-stitching. These two things pushed me to realize that it would be interesting having software that can create a pattern of a custom image or photo. I thought it would be just a fun project and an examination of my abilities to develop algorithm related software. It would also give me some experience in product design and customer relationships.
The fun was started immediately. I’d just had a painful problem - how to name a new application. The solution comes from the Frech word mouliné that means a type of floss used for cross-stitch and Maranello - a town in Nothern Italy known worldwide especially as the Ferrari home. "O" letter at the end was changed to “a” to make the title to be heard more like a female name. So Moulinella appeared!
Tags: Flash, AsWing, HaxeNeuQuant
Playing basketball with my friend (UI designer and partner) we talked about my idea of Moulinella. He found that it could be also a beneficial experience for him to create UI for an application. We chose to develop a web site. It seemed an internet application could find more users than a desktop application.
I had a lot of experience in creating web applications. Several years I used Haxe/Flash Player and C#/Silverlight to create frontend parts of RIAs (Rich Internet Application, a popular term that days). Flash Player was more spread than Silverlight so Flash became our platform. I also took AsWing UI lib for Flash since I was the most familiar with it. AsWing was written on ActionScript. So it was added to the project as SWC and Haxe externs were created.
The core Moulinella feature is converting a picture to a pattern. Technically saying transforming a bitmap to a bitmap with smaller size and smaller colors palette containing only colors that have corresponding threads. Reducing the size was implemented in Flash so it was reused. Reducing the color palette should have been implemented. The first task was to select an algorithm for color quantization. There were considered octrees, K-means and Kohonen neural networks. The neural networks won because it was nearly effective as others but faster. So its open-source implementation was ported from C to Haxe as HaxeNeuQuant.
Tags: AlivePDF, FlashDevelop, swfmill, Haxe 2
Obviously, our users didn’t want just quantized bitmaps. They want printed patterns that have usable information about points in the cross-stitch matrix including threads codes, colors, lengths. We needed to prepare our generated patterns for printing. PDF was selected as a format to layout pattern information and to give users the ability to print. Fortunately, the Flash ecosystem had the AlivePDF library for these purposes.
The development had been started. So here are some points about the development environment:
FlashDevelop became a Haxe IDE that fit my needs.
Almost the whole build process was covered by FlashDevelop. Only putting the external libraries to the resources usable from a Haxe application was covered by the swfmill (command-line tool that generates SWF files) in a batch file.
The repository was Mercurial in Bitbucket Cloud because Bitbucket gave free private repositories.
I wanted to spend as less money as possible. So the good idea was to reduce the cost of the server infrastructure and do all heavy processing (the pattern preparation) on the client-side. The whole architecture was extremely simple:
Apache HTTP Server
Static HTML, CSS
- Flash application
Tags: Flex, compc, AlivePDF, AsWing, Windows batch scripts
I started building AlivePDF and AsWing from the source because wanted to have the latest versions. So Flex Compiler (compc) became a part of the environment. A couple of batch scripts were added to compile libraries and resources with them.
Tags: PHP, Haxe Remoting, MySQL, ActiveRecord, record-macros
It wasn’t proved that the app created cross-stitch patterns well enough for users. It needed some user’s feedback to discover the quality of the algorithm. Here more server code appeared. The server code was written on Haxe as well. Haxe Remoting was used to communicate with the client. PHP was selected as the target platform since it’s easy to configure and it’s available everywhere. The database was also pretty understandable - MySQL. I used the ActiveRecord approach to communicate with DB that was a part of the Haxe standard library (Since Haxe 4 it’s a separate library - record-macros).
Tags: HXML, Ant, Java
I used FlashDevelop and batch scripts to build client, server, and resources so far. It was becoming challenging to manage the build process and keep the accurate sequence manually. I had already used the Apache Ant build processor and it looked suitable for the case. So it was chosen.
The compilation parameters moved from FlashDevelop project files to HXML files. So the compiler executed by Ant script could use them to build modules. Libraries and resources building also moved to Ant. So I got a single command to run to build the whole system.
Clearly, you can not make an application better without knowledge of how the users use it. So every important feature in the client was wrapped by Google Analytics call. Flash ecosystem had the gaforflash library for this purpose.
Tags: AsWing, haXeAsWing
Great news! AsWing was ported to Haxe by its owners. Let’s get rid of a bucket of ActionScript code and jump to the haXeAsWing library. Unfortunately, AlivePDF and gaforflash still practiced ActionScript.
Implementing an editor for cross-stitch patterns didn’t add any new libraries and approaches. It grew the server and client sides. A lot of features were implemented on the server using the ActiveRecord approach: storing users, sessions, patterns. The client was extended by painting features.
Tags: Ant, XML, XSLT
Moulinella became multilingual. Actually two lingual - only English and Russian. All strings were put into separate XML files for different languages. These files were used to create a Haxe source file using XSLT every build using Ant.
Tags: FlashDevelop, IntelliJ IDEA
I was a FlashDevelop fan for several years but changed a platform from PC with Windows to Mac. Running FlashDevelop was really tricky via a virtual machine. I thought it was overcomplicated and found IntelliJ IDEA with the Haxe plugin. It worked for me at that time and works now.
That year I didn’t invest much time into Moulinella because of my other initiatives. But I’d got a student to grow for other projects. He was offered to attend an internship in Moulinella. He proved to be an extremely productive engineer. His job was an implementation of interconnection with the threads shop and running our application inside Russian social networks (VK, Odnoklassniki).
Just maintaining and getting users ;)
There were other ActionScript libraries in the project: AlivePDF and gaforflash. AlivePDF was ported to Haxe by me - openfl-alivepdf. gaforflash was replaced by another Google Analytics library written on Haxe - haxe-ga.
Tags: jive, MVVM, Data Binding, HML, BindX
In 2015 HaxeAsWing seemed outdated since it adopted ideas from Java Swing officially released as a part of JDK in 1998. There happened new approaches as Model-View-ViewModel, Data Binding. I believed new ones were cool and could help develop faster and with a better attitude. So started adoption of them into HaxeAsWing. The result was named Jive - a cross-platform UI framework for Haxe. Views were XML files similar to MXML and XAML. HML library was used to generate Haxe code based on these XML views. BindX library brought the Data Binding between views and view models to life.
Tags: jive, openfl-snapsvg, Snap.SVG
Flash and OpenFL were widely used for 2D games with bitmap assets. So OpenFL had the bitmap nature inside such as images, textures, the canvas. Moulinella and Jive had a vector-based core. It felt more natural to use the SVG engine of a browser to show Moulinella’s UI. OpenFL provided the ability to extend its backend to render core graphics. So did I implementing openfl-snapsvg - a backend that used Snap.SVG to manipulate SVG content of a page and display whole graphics provided by OpenFL: points, lines, paths, text fields, etc. Demos are still available and working.
As a result of experiments and the commitment I’ve described above, I became a speaker on World Wide Haxe Conference 2015. The first time I talked publicly in English took place in Mozilla’s office in Paris. Here is the video of my speech "Jive: the renovation of AsWing".
*Tags: *С++, hxcpp, hxargs, openfl
Moulinella was only a creation tool so far. But a lot of content created using other applications could be found on the Internet. I believed that giving the ability to open and create PDF of patterns in different formats would bring more users than just a tool. So I started working on a converter from other formats to ours. Firstly this was a command lime OpenFL application that used the C++ target and compiled natively to the specific operating system. The hxargs library was really helpful to deal with command-line arguments.
Tags: speeches, innovation, failure, SVG, CLI, Neko
I had participated in the development of several other applications using Jive. It caused the appearance of a group of developers that were using Jive (5 persons). Doing that projects we discovered Jive’s weak parts and decided to fix them. The pivoting brought us to the redeveloping it from scratch as a cross-platform UI framework for mobiles. We presented our results on World Wide Haxe Conference 2016 in Paris. My colleague Maxim Bekhterev was a speaker at that time with me as an underground wizard. Here is the video of Maxim’s talk.
Later in 2016, we stopped working on the next version of Jive. The causes were:
the applications based on Jive were not as good as developed on official platform languages and frameworks. The main points were the lack of performance and high power consumption;
there were no new Jive related projects;
it was quite tough to develop and maintain so large library as a UI library.
Moulinella still existed and I decided to adopt some new features invented for Jive 2.0. E.g., the SVG component and the CLI. CLI provided the ability to put HML views anywhere in the project. It was also written on Haxe and used Neko as a target as other haxe libraries CLIs.
Tags: Job queue, Concurrency, hxcpp, Sphinx, MySQL
Moulinella project provided the ability to learn for two more developers this year. Their work elaborated on the idea to make Moulinella a more content-oriented product. We tried to crawl the Internet to find cross-stitch patterns and provide them to our users. It would be cool if they could search and open cross-stitch patterns from the Internet in the Moulinella application.
The interns developed a crawler, a supervisor with a job queue based on MySQL. Both the crawler and supervisor used the C++ target. There were some issues with the concurrency solved by job ID sharding to separate workers. Another part was searching. Sphinx helped us to index the cross-stitch patterns metadata. It allowed us to implement a full-text search through patterns.
Tags: openfl-ios-networking, extensionkit
Starting in June 2016, Apple required iOS apps to support IPv6. At that time the issue was not about Moulinella but the code that was developed for other projects would be reused in Moulinella later.
The problem was that hxcpp didn’t support IPv6 completely. There was no space to wait so I developed an OpenFL extension (openfl-ios-networking) that extends haxe.Http class using calls to the native iOS HTTP framework. That was enough to cover our needs for client-server communication.
This extension was based on the OpenFL extension kit which gave the common functionality such as callbacks.
Having several crawlers, a supervisor and indexer made the development and deployment more complex. We started using vagrant to configure and test the crawling-searching environment.
Doing our experiment with crawling we discovered that the most cross-stitch patterns on the Internet are copied and published illegally. I believed and believe it would be not fair to help to spread these cross-stitch patterns over the Internet. So the crawling-searching work was stopped.
Tags: hxcpp, openfl, Xcode, iOS, Android.
What if not to print PDF cross-stitch patterns on paper but put them into smartphones or tablets that are always with people. What a great idea I realized that year. Moreover, OpenFL applications could be compiled for iOS and Android without any difficulties using the hxcpp library. Especially because of Jive that was platform-agnostic UI library dependent only on OpenFl API. So the next goal became to make Moulinella mobile apps.
The main challenges were:
to support touches instead of a mouse;
smaller screens than desktops have.
So all these problems were solved and packed into Jive’s mobile theme.
At that time the build process and especially deploy process included Xcode as a tool to debug and to publish iOS applications. The android application build process covered just by OpenFL tools.
Moulinella had been a non-profit web service for 8 years and I became getting less fun from it as earlier. Keeping it the same didn’t work for me. I needed some changes to begin earning money. I believed that there is a thing better than great service and it’s great content. So I started to find a way to provide great content (beautiful cross-stitch patterns) and great service to our users and ask them to pay for it. I found a new partner, a pattern designer/seller, who already had a pool of patterns to sell.
Thinking together we determined new features to develop:
private sections on web site for users and designers;
a new service to mark the progress of stitching. It would go to our apps on all platforms and make the patterns interactive.
Since we were going to sell patterns it needed to upload them, convert to our format, prepare the metadata, handle versioning and move to the shop. These type of features causes appearing a new piece of software - a designer dashboard. It seemed the one with tricky logic but didn’t need to be cross-platform since they prepared their patterns on the desktop computers and it’s easy to open a browser and upload pattern using a web application. So the decision was to build a designer’s dashboard just as a web application.
The build process of such a mixed application worked a bit different. It involved npm to get modules then haxe to compile and then browserify and uglifyjs to prepare a resulted JS to run in the browser. All these steps were put into my ant script so we still have one script to build the whole system.
Tags: Haxe Remoting, HaxeTink
I’d already wrapped Haxe Remoting with callbacks to create service clients. They said there was an approach that gives a better definition of the control flow of asynchronous logic, reduced coupling, better error handling, and improved readability - promises. So adopted them using Tinkerbell for Haxe.
Tags: student project
A lot of haxe-react related work was done for our student open-source project and then adopted for Moulinella by two smart interns. They moved their solutions for UI layout, authentification, and notifications.
Tags: smtpmailer, asys
Owning a mail server was a pain. It’s hard to be sure that it was not in the blacklist and our messages accepted by recipients’ servers. So we moved to a third-party email service it gives us confidence about messaging and brought a couple of libraries: smtpmailer and asys.
Tags: Apache HTTP server, hxcpp, Docker, Job Queue, RabbitMQ, AMQP, Cron
When a designer uploads a pattern or changes its metadata it needs to be checked, transformed to our format, thumbnails and previews should be prepared, PDF should be generated and maybe emails should be sent. And all of it for different languages. Certainly, it would take some time and it shouldn’t be doing during an uploading HTTP request.
So I moved long job-related features to a separated module - worker. It can be run outside the Apache HTTP server as an independent service and receive signals from other parts of the system through RabbitMQ. It was brought to life with the help of the hxamqp library and my commitment to support Haxe 3.
Having workers compiled natively for a specific operating system would give a performance reward. It’s a great feature of the Haxe code and hxcpp to be compiled to the C++ target and then to a platform. There might be an obstacle if the build system is different from the target one. My build system was Mac but the target was a Linux server. Docker came for help here. It took some effort to configure the docker environment and put it into the Ant script. But it gave the ability to create Linux executables on Mac.
The job initiators were our web site users so far. But the features like subscriptions needed the system itself to be a job emitter. Here the Cron Linux service came to run several jobs regularly.
Tags: fastlane, compiletime
Xcode and Google Play Console site were used to deploy mobile builds for testing and publishing. It was a certainly time-consuming manual operation. Being in 2018 on Haxe US Summit in Seattle I heard a talk from a FlowPlay developer about how they adopted Fastlane for their Haxe projects. So did I and became completely happy doing it in one click.
After deployment the good thing to give users (especially testers) the ability to identify what the version of the app exactly is. The compiletime library gives you the ability to put dates, versions to the code during the compilation.
Tags: extension-iap, extension-admob, Objective-C, Java
Moulinella was a free mobile app for years. Now we were planning to make some money adding a premium subscription and showing commercials. Corresponding libraries were found extension-iap and extension-admob. They needed some fixing so it was done by forking them: iap, admob.
Tags: Display List, Bitmap, Tilemap, Graphics.drawPath, 100K tiles
The main feature of the mobile apps was a cross-stitch pattern viewer. It needed to display a matrix of square areas covered by a layer of lines and curves. This feature should be very dynamic. We must have the ability to navigate, scale, disable layers, make the squares or lines partly transparent.
In the beginning, it was implemented by creating two bitmaps by drawing on them square objects and lines on the fly and putting the bitmaps into the OpenFL display list one on another. Even the first testing discovered performance issues.
After several experiments, we found a combination that was enough for us:
Not create an almost full-screen size bitmap but create a smaller one with distinct parts of the pattern’s matrix and use it as a Tilemap.
Filter squares and lines that shouldn’t be rendered in case of scaling.
Cut lines to fit the screen in case of scaling.
Use batching to draw lines (Graphics.drawPath).
These changes gave the ability to show the patterns with about 100K stitches. We even met the Tilemap limit of 2^14 of tiles. Having several Tilemaps helped. It didn’t give 60 fps but it worked well enough.
Tags: Dynamic Web pages,** **Paypal, Yandex.Checkout, Tax online services, Haxe Web dispatcher, Haxe Templates, PHP
The current architecture didn’t include the HTML generation on the server. We just had static pages, web services, and huge JS clients. When the shop occurred in our plans we started thinking about SEO and giving the users and search crawlers identic and relevant content. So our servers started generating the shop HTML pages by a new PHP application configured via mod_rewrite to process relevant HTTP requests. I adopted a simple web dispatcher component for routing and Haxe templates to have already prepared HTML parts.
Since we were planning to sell subscriptions and patterns it needed to have some ways to charge users. We selected Paypal for worldwide users and Yandex.Checkout for Russians. Russian laws also said to have an online integration with the state tax service to give them the ability to check your cash flow.
I’m not sharing much here or opensource the code but it needs to be mentioned because of effort to make this integration happen. And yes it’s possible using Haxe externs to adopt Paypal and other services SDK.
The client code related to authentification, accessing services was in the big JS application - the user/designer dashboard. But the same features were needed for the shop pages to do small things with our services (like getting some information from the server, filling a cart, etc). Taking the large JS file to the shop pages looked weird so I created a separate JS module from the same code base taking only specific classes and functions. That module has a name - Capsule.
Tags: extension-facebook, extension-vk, extension-appleid, Objective-C, Java
Users mentioned that they wanted to log in through their social network accounts. We found the most popular variants were Facebook for international users and VK for Russians.
So I started integrating them into the application. The web part was the easiest one. Each of these social networks had JS SDKs that could be simply used with externs to put them into Haxe codebase.
The integration of them into mobile applications was a bit tricky. Especially because there was only an OpenFL extension for Facebook. Even this extension needed some rework. So my fork of another fork appeared. VK extension was created based on the Facebook one. I was so happy when Apple brought me to support their Apple Sign In because of its obligation. Here is the extension-appleid I developed.
Tags: C++, hxcpp, Neko, Haxe 4, hx3compat
On some big patterns, we’d got crashes accessing wrong memory addresses. It seemed that GC worked wrong. I worked it out for one pattern by tuning hxcpp GC by its parameters to do more checks. But tester found patterns where it didn’t work. I was completely confused. This issue affected the C++ target: mobile applications and workers. Thanks to Haxe multi-targeting the worker moved to the Neko target and the issue disappeared. But it didn’t work for mobile apps because C++ was the only case for it. At least I thought so. Correct me, please, if I’m wrong in comments.
Lately found that the other developers experienced similar issues with GC that time but with Haxe 4. I was still on Haxe 3. After some time the solution was found by hxcpp maintainers and committed into the latest version that was for Haxe 4 of course. That was a super driver to upgrade the Haxe version.
I could say that this update went smoothly especially because of the hx3compat library. There were a few changes in my code and libraries. Even the issue in the Haxe standard library was found. But I must say very thank you to the Haxe team for their effort to fix issues and deliver the next Haxe version.
Some recent smartphones have a notch. Android skips a notch by default. That’s OK but iOS doesn’t. As we don’t support it our UI looked ugly on iOS. Obviously, we should use safe areas for these devices. Luckily the library had already implemented - extension-safearea.
The last development cycle has been finished. So it needs to look behind and do some retrospective and go further and think about new opportunities. This article is a part of it. Thank you for your comments!
create cross-stitch patterns by photo;
save patterns on the server;
show patterns and save progress on them.
Linux server: PHP, C++, Neko
HTTP server: Apache
PHP Apache module
Viewer / Wizard / Editor
Dynamic HTML pages
User / Designer / Admin private sections
Approaches / Other tags
Tax online services
Haxe Web Dispatcher
HaxeTink (Tinkerbell for Haxe)
haxe-react + ReactJS
haxe-react-router + React Router
haxe-redux + Redux
Build / Deployment tools
Used but passed away
"Jive: the renovation of AsWing" on World Wide Haxe Conference, 2015
"Jive: UI for mobile platforms" on World Wide Haxe Conference, 2016
Thank you for reading till the end! Hire me if you’re looking for an engineering manager or an engineer with the experience above. Full my profile is accessible on LinkedIn. You can contact me via firstname.lastname@example.org.