Table of Contents
Introduction
blockpit began as a challenge to myself to create a unique code environment completely from scratch, or at least just using minimal dependencies like React and some global state store. I distinctly remember that Zustand was recently released, and since I absolutely hated Redux I knew I had to try it
like a good JavaScript framework junkie.
Next was finding out what kind of editor to build. I wanted to build something fun and approachable, which would hopefully inspire others to code. I remember text editors and the stuff I would type into them being incredibly boring and intimidating when I learned to code, so I decided to fuse two ideas together:
- a block code editor, for a more intuitive and friendly vibe
- creative coding, or creating art with plain JavaScript!
Target Audience
Learning to code can be hard, but well designed tools can make the journey significantly more enjoyable. blockpit was made for anyone who wants to learn to code by making art.
Unlike other popular block code editors like Scratch, it is also possible to export the preview as a standalone HTML/CSS/JS file. I intentionally designed this feature to help reduce some of the stigma that block coding isn't "real" coding - it is, and can produce real and meaningful outputs!
Features
Although incredibly difficult to actually implement, blockpit's drag-and-drop block system is intuitive and self explanatory. Indeed, much of blockpit's power emerges from the far "smaller" but no less complex features.
First, blockpit includes a fantastic file system that supports pretty much all common file operations: drag-and-drop files around, rename them, nest them, or upload any image to use with p5. Uploading and storing images happens entirely on device - p5's loader functions are essentially adapted to load them from this pseudo file system, rather than a direct URL. This also allows users to export the entire project as a single, unified .html
file that includes all relevant resources with a JavaScript-based "file system".
Loading an image with blockpit's file system!
For even more flexibility and power, blockpit allows raw JavaScript directly with the unsafe
block. This also enables more experienced users to include and work with additional dependencies and features beyond p5, without having explicit block. blockpit also comes with a series of templates to get started with p5.js, such as this neat grayscale sunset (which also showcases the unsafe
block).
Using the "sunset" template
Design
blockpit's editor design is informed by Codepen, VSCode and (2022) Replit. The vertical line of icon buttons mirror VSCode and (2022) Replit's activity bar, and the three separate "panes" (files, block code editor, and preview) are incredibly similar to Codepen's fixed layout.
blockpit has the fixed resizable panes of CodePen
... and the activity bar of (2022) Replit
Tech Stack
I tried to take "no libraries" as seriously as I could to make the project as educational as possible, while ignoring the obvious bloat bucket (cough React cough). Unfortunately, this meant no Blockly or even a fancy drag-and-drop framework like dnd-kit, which would make a block code editor outright trivial to make (and possibly both faster and better quality). As a result, the only technologies invovled are React, Zustand, and Tailwind + my own UI library.
Challenges Faced
Nearly every feature I implemented proved to be incredibly challenging.
In 2022, I lacked much of the necessary knowledge about data structures that would have made implementing a file system and block code editor significantly easier - namely, the existence of trees! Nearly everything in blockpit is based on trees - files are stored in trees, the code blocks are essentially trees with many children (for both the arguments and body of a function, for example), transpiling blocks requires traversing the block code tree, and so on.
In making this project, I had not only accidentally discovered trees, but I also stumbled upon recursion. Recursion is used practically everywhere across blockpit, from rendering the contents of folders and code blocks, to traversing the entire workspace to find possible conflicts that should not be allowed (such as dragging and dropping a block into itself). Finding these conflicts was a nightmare. There were so many possible edge cases I didn't even consider when I started the project - dragging blocks into themselves, into their arguments or children, dropping a block on top of another block (I figured this should "swap" them), and so many more that I can't possibly remember.
Transpiling blocks, then, was simply a matter of traversing the block code tree with recursion. Fortunately, because blockpit closely mirror JavaScript syntax, no additional transformations were necessary outside of recursively converting each block (and their arguments and children) into a a string. My implementation definitely isn't pretty, but it certainly works as a literal translation. Here's an example of the (admittedly horrifying) output. No syntax highlighting - to emphasize the horror.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let denoImage
function preload() {denoImage=loadImage(_findFile("/deno.png")
)
}
function setup() {createCanvas(Number("400")
, Number("200")
)
image(denoImage
, Number("0")
, Number("0")
, width
, height
)
}
Current Status
I initially began this project back in 2022 (v1), but I only recently (September, 2024) modernized and upgraded (most) of what was previously a garbage code base to v2. Just as an example of how inexperienced I was with React and TypeScript, I thought that .d.ts
files were required to add more complex types and frequently employed React no-no's like conditional hooks. Interestingly, blockpit is also one of the only projects I've maintained for multiple years just for fun - I never really had much users or feedback to respond to. At least for now, blockpit is in a passable state relatively free of linting errors or beginner React mistakes - just ignore the any
types sprinkled around. It's also more accessible because it includes my Replit-esque design system built on Radix Primitives.
Overall, v2 is significantly cleaner and more lightweight (for example, I removed framer-motion
from the bundle because it was only being used to animate in the intrusive modal shown below). I also added more templates to showcase the power of blockpit, as well as additional blocks for convenience (including better loops, variables or identifiers, and more). Here's what v1 looked like:
Welcome modal, blockpit v1
Using a template (in dark mode), blockpit v1
Future Enhancements
blockpit has plenty of room for growth. I still need to upgrade and refactor the codebase (particularly the names of certain types and methods, adding much needed documentation, and some edge cases that I can't seem to reproduce). There's also (too) many new features that would make blockpit more enjoyable to use. I've provided a list of what I hope to implement in the future, but it's difficult when I have so many other (side) projects I want to work on.
- Draggable tabs that can be reorganized all over the workspace, instead of using
react-split
- Transpiling blocks could be far better
- Hover over blocks for documentation
- Formatting code exports with prettier
- Analyze dependencies and create blocks from them
- More blocks:
- Arrays aren't currently represented and require using the "unsafe" block (for JS interop)
- Imports or some way to organize the order in which
.block
files are imported
- Save workspace in local storage (reloading the page will cause all work to be lost if you haven't downloaded your
.block
files)
- Download the entire workspace as a
.zip
file