When coming up with ideas for new ways to solve problems in my code, there are usually a number of unknowns. As I brainstorm solutions, I don’t mind a bit of hand-wavy magic over certain portions of the solution, but I find it valuable to be honest about which parts I’m sure of, and which parts will need some more investigation.
I was recently thinking about an experimental brushing engine that I’m working on for Pigment, the coloring book app that I work on, and had an idea for a new way to draw brush strokes that had the potential to use far less memory and be able to draw faster. While I had the rough idea for how this drawing would work, I wasn’t exactly sure if the code could be structure in a way that would give me the performance gains I had hoped for, so it was time for a prototype.
Answer the Unknowns, Quickly⌗
In this instance I could have made a branch in Pigment’s source code and started hacking, but that would have required some time investment since the idea was still rough and still had some open questions. The app is already structured around the existing brushing engine, and includes a ton of features and code that aren’t relevant to the questions I needed to solve. Not only that, but the time it takes to build and install the app, then navigate to the editor to be able to try out the new brushes would really add up. Before committing this time I needed more confidence that my idea had the potential to deliver on my hopes.
Since mine were graphics questions, I turned to ShaderToy to prototype my solution. ShaderToy allows you to edit graphics (or audio) shaders and see the results in real-time, right in the web page. There, I was able to trim my prototype down to it’s most bare components to start experimenting quickly.
Using the super fast iterative cycles of so many things on the web, I was able to flesh out my idea in code and see it running in just a couple of hours. Avoiding the compile times of native apps and the architectural overhead of a large project allowed me to quickly learn that my solution could work, and the performance, while still an open question, had the potential to be feasible on high-resolution mobile hardware, so I was ready to take the next step.
Increase Fidelity as Your Confidence Grows⌗
Now that I was confident that I had a workable solution and the performance seemed promising, I was ready to take my prototype into the app. Since the brushes that Pigment uses are implementations of an interface, I was able to create a new implementation of a Brush, copy the shader code from my prototype with minimal modification, and try my new brush shaders within Pigment right away.
While the Brush that my painting engine uses is a somewhat simple interface, Pigment’s existing brushes include a fair number of settings and logic, like which texture to use for the brush head, the relative size and spacing of each stamp, and the logic detailing how to stamp each point based on user input. My initial version simply used hard coded values, but I was quickly able to make a version that would wrap my existing brushes so that I could try this out with real world brushes.
Since I was able to write the majority of the code in ShaderToy, this was really just an exercise in wiring things together. I was able to get the real experimental parts of my idea worked out on a platform that offers 0 second compile times and iterate quickly, then simply copy that code into the app and plug in the right data.
The most important part of this approach is that I didn’t have to change much of the app. For this simple prototype, most of my brushes wouldn’t work, and much of the UI for adjusting brush settings wouldn’t change anything. But that’s okay, because what I really needed was to get incrementally closer to a final solution quickly, so that if things couldn’t work I could find out as soon as possible.
Give Yourself Escape Hatches⌗
Once I was able to try out my new brushes in Pigment on a real device I discovered that the performance was not what I had hoped. While there were gains in the memory footprint, and the time spent uploading data to the GPU, the rendering itself simply wasn’t fast enough on a high-res mobile phone to make for a good user experience.
Performance was a concern all along, so after spending a little more time looking for some obvious issues or optimizations I could make for big improvements, I decided to table this idea.
Throw-away is Okay⌗
Determining that this idea that I was excited about wasn’t going to work was disappointing, to be sure, but that’s okay! Because I chose prototyping tools that allowed me to iterate and quickly get to something that I could experience for myself, I wasn’t forced to give in to the uncertainty that clouded the initial idea, or invest huge amounts of time upfront that made it extra painful to change directions.
For different problems there are different ways to prototype and speed up your iterations. You might write your business logic in standard JVM code so you can run it directly on your desktop, or create a small sample app that only loads a single module of your app. However you do it, I hope you find that prototyping allows you to test out more ideas and move faster.