Canvas Computing Prototype 1
I've been working for a while on some new ideas around the future of programming, but I haven't done a great job of sharing these with the world. It's not science if you don't publish your results, so here we go.
I have long wanted a large infinite canvas for computing. A place where I can sketch, draw, and drop in code, all positioned exactly how I want. I should be able to divide my program into little chunks and algorithms, mixed with interactive controls and visualizers for the output. While the idea seems similar to boxes and lines visual programming environments, mine is still firmly built around a text based language (in this prototype, anyway). I'm imagining something more like a spreadsheet where you can place your data and your functions wherever you want, tied together with dependencies so any change will automatically recalculate the results.
Okay, that's all pretty abstract. Let's discuss a very simple, but concrete, example.
I want to draw a square. I have a canvas view for the output, and a tiny text editor containing a few lines of code. To specify the color I don't want to put it in the code, but instead use a little color picker button. When I change the color the code should automatically execute and update the canvas. This week I built a prototype that can do this:
Here's what it looks like:
Each of the panels can be dragged around in an infinite canvas. Not shown is a debugging panel and little menu bar.
Functionally it works. Everything can be moved around, the code executes, and there are dependencies so that when you change the color the code runs and updates the canvas. Most importantly, the whole deal is persisted as a JSON document into the browser's local storage and reloaded on page load.
Now let's discuss what the problems were.
I built the prototype using React and Typescript. I tried to have a base class for handling events so that the different coding nodes can communicate with each other. As part of this I want the type system to only let you listen for events that the source will actually send. Making this work with inheritance under Typescript is tricky. At one point two versions of what *thought* were the same type were incompatible and tried to insert a 'never' type. This blog shows a better approach: Events and listeners in TypeScript (Christopher G. Jennings that I'll try next time.
Next, The ceremony of which node is watching which is complex. It's currently done with the aformentioned event system. There has to be a simpler way to express dynamic dependencies. Also, the system itself needs the ability to monitor all of the watching links and show it in the debugger. How can we find out which objects A is listening to, and which event names? So, new design is needed here.
The persistence system is flaky. We need an object model that can be persisted more reliably. Maybe empty constructors. The problem is that the constructors of the classes do computation, but it's a lot harder to call those same constructors programmatically when restored from JSON. Instead I'd like to be able to allocate the object with the right prototype, restore all of the property values, then invoke some sort of 'init' method to make the object live. Only then will it be shown on screen and start propagating events.
Another problem with persistence is that the dependencies are not preserved across persistence cycles. And I have to register a fixed list of classes and views to make persistence work correctly. Is there a better way to automate this part so it needs less overhead? Also the debug console and menu shouldn’t be persisted since they aren't really a part of the user's document.
Instead of a menubar I'd like to be able to create an object by right clicking, selecting from a menu, and the new object appears there. This lets me create an object and give it a position at the same time.
A few other issues:
- The snippet editor needs syntax highlighting.
- The color constant is too big. It needs a different, maybe non-resizable, window view. Still must be draggable, though.
- The persistence engine needs to know when nodes change and when they move, but other nodes only want to know when a dep changes, not when it moves or is resized.
- The system needs a way to add notes & comments and headers & captions.
For the next iteration I want to address the issues above, but with a more complex use case. I'm considering some floating constants that are for numbers used to draw a fractal tree. Then you can just move some sliders to change the parameters to the fractal.
Posted December 3rd, 2022