Shelly: under the hood
Tags: learntocode, javascript, turtlegraphics, programming
Published: 2021-03-25 19:36:50

[Shelly](https://shelly.dev) — a programming language for drawing — is one of our side-projects that we work on in-between [other engagements](https://softwaremill.com).

It features the **challenge** mode, where through a series of step-by-step tutorials you can learn both the Shelly language, as well as the basics of programming. There's also the **creative** mode, where you can draw whatever you like, and share it with others as an image, code (to allow modifications), or as a custom challenge — can others reproduce your painting?

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6p9ctganwffui48o1kul.png">
  <figcaption><a href="https://shelly.dev/p/adamw/47fc197c3154">https://shelly.dev/p/adamw/47fc197c3154</a></figcaption>
</figure>

Shelly delivers the well-known concept of turtle graphics in a modern package, with a friendly interface, instant drawing, rich sharing options, and gamification. You write the program, and Shelly the turtle immediately reflects the new code on the canvas — live programming!

The Shelly language is a mix between [Logo](https://en.wikipedia.org/wiki/Logo_(programming_language)) and more modern programming languages. Due to its educational character, it's kept simple: we've got expressions, named functions, loops, conditionals, recursion, and that's about it! Still, even using these basic constructs, you can create some really interesting images.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fes346rf9gfddutaekju.png">
  <figcaption><a href="https://shelly.dev/p/adamw/27e55b6e028d">https://shelly.dev/p/adamw/27e55b6e028d</a></figcaption>
</figure>

You might be wondering: what's under the hood? What kind of technologies can one use to build such a live-coding environment?

Shelly itself is built with [TypeScript](https://www.typescriptlang.org). The types bring some sanity to the otherwise untyped JavaScript/browser domain, integrating nicely with the whole rest of the ecosystem.

For the skeleton of the application, we've made a popular choice, that is [React](https://reactjs.org). Nothing non-standard, though as the application has grown, we've seen that our reliance on [Context API](https://reactjs.org/docs/context.html) to manage state has reached its limits. That's why we're considering porting that aspect to [Redux](https://redux.js.org).

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tqzrtnmql5bqg0qnlhnn.png">
  <figcaption><a href="https://shelly.dev/p/adamw/608a6168c983">https://shelly.dev/p/adamw/608a6168c983</a></figcaption>
</figure>

Second, we've got the editor. Here, we are using the [Monaco Editor](https://microsoft.github.io/monaco-editor/), which is the editor that's also used in [VisualStudio Code](https://code.visualstudio.com). To configure the editor for a custom language, you have to provide the keywords, symbols, operators and configure the tokenizer. We also get auto-complete out-of-the-box — you just need to provide the completion function, which can be context-sensitive or not.

Monaco also gives us the possibility to manage errors (underlying them and showing glyphs) and provides contextual editor extensions. In Shelly, this is used to show a palette of available colors and pen patterns, whenever the user writes `pen` or `fill`. Try it!

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jdtczqss3ublz2mmxz8m.png">
  <figcaption><a href="https://shelly.dev/p/adamw/992d937596c3">https://shelly.dev/p/adamw/992d937596c3</a></figcaption>
</figure>

Monaco has quite good documentation, though sometimes you'll end up digging through GitHub looking for usage examples to see how to best configure the parser, provide styling or manage the web workers.

Let's move to the canvas. Here, the main job is done by [Konva](https://konvajs.org), which provides us with a simple and performant layer on top of the raw HTML `canvas` element. Shelly uses a rather basic subset of Konva, as Shelly's drawings are composed mainly of lines, arches, basic figures, and SVG patterns (hearts, stars, cars, etc.). One aspect that was especially challenging, however, was properly handling the zoom & pan, using both on-screen controls and the mouse wheel.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xb50rv5ktz8shju3912u.png">
  <figcaption><a href="https://shelly.dev/p/adamw/62d98bc06139">https://shelly.dev/p/adamw/62d98bc06139</a></figcaption>
</figure>

What about the drawing/programming language itself? Even though it's quite simple, we still need a tokenizer and an interpreter. We've chosen [ANTLR](https://www.antlr.org) to define the grammar, with [antlr4ts](https://github.com/tunnelvisionlabs/antlr4ts) as the target. That is, when running the ANTLR tool, the input is a file containing the grammar definition, and as the output, we get TypeScript files that handle the parsing of arbitrary text into an AST (Abstract Syntax Tree).

However, we are not using the ANTLR-defined AST directly when doing the interpretation (that is, actually **running** the program), but we're translating it to yet another representation, which is more flexible in the result type — and we need that flexibility to implement arbitrary-depth recursion. Here, we've used [trampolining](https://blog.logrocket.com/using-trampolines-to-manage-large-recursive-loops-in-javascript-d8c9db095ae3/), thanks to which you can loop and loop without blowing the browser's JS stack.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/th3zt0xl8khcxv0svj9f.png">
  <figcaption><a href="https://shelly.dev/p/adamw/f08a8c07a102">https://shelly.dev/p/adamw/f08a8c07a102</a></figcaption>
</figure>

The downside of using ANTLR with the TypeScript backend is that the resulting package is quite large — which means a large download, and a longer wait for the initial page load. That's why we might consider writing a parser by hand in the future, which won't have any third-party dependencies.

Shelly's frontend is built using [webpack](https://webpack.js.org), tested with [jest](https://jestjs.io) and [playwright](https://github.com/microsoft/playwright), and deployed using [Netlify](https://www.netlify.com). The whole process of connecting a GitHub repository, configuring CI, [pull request previews](https://www.netlify.com/blog/2016/07/20/introducing-deploy-previews-in-netlify/), custom domains, and, finally, the production build is really painless. And makes the development so much easier!

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4m8ge45d2gs3x1e1ra6y.png">
  <figcaption><a href="https://shelly.dev/p/adamw/b896560155c3">https://shelly.dev/p/adamw/b896560155c3</a></figcaption>
</figure>

Finally, on the backend, we've got a simple [Scala](https://www.scala-lang.org)\-based application, deployed on [Heroku](https://heroku.com). The database is [PostgreSQL](http://postgresql.org), but the application is not a straightforward CRUD as you might expect, but instead uses [SQL-based, transactional event sourcing](https://softwaremill.com/entry-level-event-sourcing/). Probably overkill, but — if we ever want to create a new view basing on the events (which include e.g. program creation in creative mode, or solving a challenge) — for sure we'll have the data available!

And that's it. [Give Shelly a try](https://shelly.dev), solve the challenges or create a drawing in creative mode. Then, let us know what you think, what you'd improve, change or add!