 I have a new programming thing, so I'm not going to do a workbench video today because I haven't really been doing any hardware for a while. Instead I'm going to demonstrate a software thing that I've spent a few weeks prototyping. Now the context behind what I'm about to show you here is that back in the early days of computing there were two different ways to write programs. One was the one we use today where you write source code and you compile it into a binary and then you deploy the binary and people run it. But the other one which was exemplified by systems like Lisp and Smalltalk was a whole lot dynamic. Instead of having a dividing line between programmers and users, every user was their own programmer. They could take the lid off the application they were currently running and tinker with the innards. And this is my attempt to prototype such a system using modern web technologies. And even though it's essentially made out of farmers string and baling wire, it actually works pretty well. And I am going to demonstrate this by live coding a very simple and terrible game on this system and show you how it works. So the first thing you need to be aware of is that even though this is running in a web browser, everything you see here is running locally. It's not making any network requests at all. There's no server-side component. So what we're looking at here is a very simple desktop. We have a menu here that allows you to instantiate various things. For example, here we have a clock and I can create as many of these as I like. But we're not terribly interested in this. The one we are interested in is the editor here. The editor allows us to edit any of the classes that make up the running system. And as we edit them, the changes we make get deployed live so that our clock here is clock runnable here and this is the code. And we can modify this code and the clock gets modified as well. For example, if we want to change the background color, all we need to do is to edit this here, hit the commit button and it changes. Notice that we haven't reloaded anything. We haven't had to even close the clock or restart it again. We just change the code and it goes live. And we're going to use this as the basis for our game. I've already done the boilerplate of the infrastructure that actually makes the game work, that is creates the canvas we're going to draw in. So all we need to do is to create a new class, which we're going to call possibly with the cap stock in the right place. We're going to call this dubious game. And now we get a new class here with natural code. And we create a skeleton like so. Now this is all working in TypeScript. There's a copy of the TypeScript compiler built into the system you're looking at here. This gives us type safety and just general nice things. But I've edited the code. The asterisk here indicates it hasn't been committed. So I hit the commit button. I'm going to get the error. Oh yes. Silly me. Why is that not working? Not rendered. Redraw. There we go. So as soon as I commit the code, the menu up here notices that some of the classes in the system have changed. And it discovers our new class. It uses the static method here to figure out is actually runnable. I did mention this was all a terrible hack. But we can now start it. And here we have our game canvas. This is a simple HTML canvas. We have a single button and a frame rate counter. So now we can actually start doing stuff. The first parameter to redraw is the HTML canvas context. So we can simply do like so. Hit commit. And it updates. The game base class watches the mouse pointer and adds instance variables for those. So we can do... And we have a box. That's not a very nice box. Let's make this a bit smaller. That looks more like it. And this is going to be a very simple bat and ball game. So we actually want this to be down here somewhere. There we go. So now we have a bat that moves to the mouse pointer. The next thing is the ball. Now the ball needs persistence state from one frame to the next. So we need some variables for that. Now this is where the reset button comes in. Because I can just hit the commit button now. And it will compile the code. And we now have two instance variables in our game. However, we haven't closed and restarted the game. Therefore the constructor hasn't run. So in fact these two variables will be uninitialized. Even though the type says it can't be. There's just like a problem with this kind of very dynamic system. Which is why we have the reset button. So now I can hit the reset button and it will initialize bx and by to zero. But we don't actually want it to zero. We want it somewhere a little more sensible. Let's try there. And of course we want to draw the ball itself. This is why I wanted TypeScript to be honest. JavaScript, raw JavaScript is so dynamic that actually trying to deal with trying to deal with this kind of typo is just a complete pain. So we have our ball but we want it to move. So we need more variables and a bit of physics and a bit of gravity. Now the delta variable passed into the redraw method is in fact the time since the last frame. So by basing this on delta we get consistent performance even if the frame rate is not even. In fact the frame rate is pretty solid at 60 frames a second. But let's commit that. Now the ball vanishes. Is that so fast? Have you made any stupid typos? That's better. That's a bit too slow. So let's change that to 100. Okay so when we hit the reset button the ball gets reset up to about here and then just drop straight down. We don't really want it to fall off the bottom. So let's have it bounce. There we go. A bouncing ball. You'll notice it actually gets a little bit higher every bounce. This is just general rounding errors. So we can very very crudely work around that. The number here was figured out by experimentation when I was trying this earlier. So we commit that reset and it should keep bouncing up to about here each time. A little bit low but change that up a bit. Okay now a little bit more physics I think. We want the ball to bounce off the bat otherwise there's no real point to the game. So we're not going to do this in a particularly clever way. We're just going to do and the bat is drawn at 350 for the middle. So the top is 340 and the center of the ball is another 10 above that. And you notice that every time I do one of these conditions I always check the direction. This is to avoid embarrassments where the ball gets stuck in the zone where it will bounce and just wobbles backwards and forwards. So we only want to bounce the ball if it's going down like so. Commit. Second bounce. It's indeed bouncing off the bat. Yep that seems to be working. Of course it's not terribly exciting. So let's have it bounce off the left and right. So if it hits the left of the screen it bounces and if it hits the right of the screen it bounces. And shall we have the top? Yeah let's have the top. So the ball is moving a bit faster than it was before. That was probably a glitch due to the pause when I recommitted. But we really want the ball to move left and right. Give the game a little bit of a challenge. And what I'm going to do is have it so that if the ball hits the left side of the bat it starts moving left and it hits the right side of the bat it starts moving right. Just to give a little bit of a challenge. So we calculate the distance from the middle of the bat to where the ball is. So if it hits to the left from centre here will be negative. So this means we can simplify this condition. So let's just check to make sure that works. Yeah that seems to be working. Ah that didn't commit. That's because I've got my parentheses in the wrong place. Let's try that. Yeah the editor I'm using here is CodeMirror. And I haven't quite figured out all the details of refilling the text and not having it reset back to the top of the page. So I can make them a little bit bigger but that won't help much. Is that working now? That does seem to be working. So we are going to let's just try that and see what happens. Okay that seemed to be working. That is like almost a game now. So see if I make the ball hit the middle of the bat then the ball moves in a predictable fashion. If it hits the edge of the bat then it moves in a less predictable fashion thus rewarding accuracy in positioning the bat. But this is going to go on forever. So we want it to get a little bit harder as we go on. Now one thing we could do is just comment out this line. So now the rounding error will cause the ball to speed up over time. You see it's now bouncing off the roof of the play area. So this is kind of this is getting harder. But there's actually another variation which I kind of like better. What we do is we add a new variable for the width of the bat and we then change this. So that is continuing to work. However every time the ball hits the bat we make the bat shrink thus resulting in the game getting progressively harder over time. Yes I actually like lost there but we'll pretend no one saw that. Yes it's not a great game but it is yes that's not brilliant. So let's actually just tweak this slightly so the ball will get a little bit faster as time passes to make it a little bit more interesting. As soon as the ball starts moving fast enough to actually hit the ceiling the dynamics change. Anyway that does actually seem to be working now. So let us replace the bounce off the bottom that's this one with just starting the game up again. We're not going to bother keeping score or anything. Okay yes it is in fact enough of a game that I find myself unable to actually play it reliably and talk at the same time which means it's like almost fun almost. Anyway here we have our game which is now semi-complete. It's a whole 61 lines of code. We have written this we haven't reloaded or reinstanshated this game window at all. This is the same game all the way throughout. We can change the game logic as we go. Do I like the color of that no. So let's change it to yellow. There we go. What about the play field? Let's try something like this. That's much nicer. Yes I particularly like the way that the red ball against the teal background is actively making my eyes bleed. Yes I think that's worth having. And of course we can construct new instances of the game. Playing more than one at a time is kind of hard. I can usually cope with two if the balls are not quite in sync. So this has just been like playing with a little trivial game but everything else you're looking at here is live updatable exactly the same way. For example this editor is like editor component here. This is it. It's only like a hundred lines of code. When we have like uncommitted text like that's a syntax error that it won't let me compile that. It highlights this as with an asterisk which isn't really particularly good. I mean we can easily change that. Here's the code that actually draws the asterisk. So let's just create some style like so instead of having this we simply apply the style like so. Oh type safety is wonderful. Right in fact I managed to perpetrate a typo here. I got these the wrong way around. So now we have a junk line and it gets highlighted in red. And the reason why it updates immediately when I change the code is of course immediately after committing the code which actually happens with this line here to call to recompile. We re-scan the classes and repopulate this thing on the left. So I hit the commit button. It updates instantly. Everything in the system is editable except the actual kernel. The thing that takes the type script here and turns it into javascript and hot patches it into the system. The kernel is, if I pull up the debug menu, is also written in type script. And it turns into 400 lines of fairly poorly written type script. The most complicated part of it is we actually have to use the type script compiler to transform the type script classes. Javascript will let you update methods on the fly, but it won't let you update object constructors. So we need to do a bit of rewriting in order to work around that. If you go back to our game here, this constructor here is actually rewritten to be an ordinary method and then we use magic to invoke this instead of the real constructor. I can show you that code. It does use ECMAScript classes throughout. It will run with ECMAScript 2015 that's currently set to 2017 so I get stuff like async natively. However, we don't use the ECMAScript class structure. The way ES6 constructs objects involves running multiple constructors and we don't really like doing that. So we have a single real constructor and we create the objects manually using prototypes and so on. And then we invoke the method here to actually make it happen. You can extend normal JavaScript classes, but it's a little bit unhappy and you need a bit of hacking. And unlike real small talk and list systems, you can't save the entire state of the system. You can hit the export button here and save all the classes, which can be then reloaded later. But you don't get any object state. So if I were to reload that, what you end up with is a blank canvas with menu. Of course, our game is there because that exists in code and you can just start a new one. But I don't really see a way to do that in JavaScript. The system just doesn't really handle it. Nevertheless, what I've come up with here is actually surprisingly useful. There's plenty of things I can do to it. For example, why is editor component not committed? Did I remember to press the button? Are there any errors? It doesn't show any errors, but it's still showing up in red. Yeah, there are some bugs, as you can tell. Yeah, I'm not sure what happened there. Interesting. Yes, there are bugs. There are things I can do. For example, the editor is CodeMirror, which is a perfectly capable editor. But if I were to use Microsoft Monaco editor, which is designed for TypeScript, I would get basically the Visual Studio Code TypeScript auto completion features for free. The TypeScript compiler is running in this web browser. So it's got all the type information about the code. So that would provide a significantly nicer actual editing experience. But Monaco is a pain to integrate as you can't load it from a CDN. You have to use the terrible JavaScript build systems and Webpack and stuff like that. I really just couldn't be bothered. The server side part of this is just the simple HTTP server in Python, like the one out of the box, and a bunch of static files. That's it. There is nothing else. Other things it could really do with is, you know, nice UI. As you can tell, I put very little effort into this, like all the padding is wrong. Yeah, you shouldn't be able to do that for a start. There is no sandboxing or security. All the code here is run as if you just hit F12 and type stuff into the console. So if I were to add a feature to allow you to merge other people's code into the system, that would be a bit of a security flaw. It's be possible to work around this. Web workers provide pretty good JavaScript sandboxes, but Web workers don't give access to the DOM, so code in a Web worker wouldn't be allowed to talk to anything else. So it would need some kind of proxying through message parsing, which I've had thoughts about, but I'm not sure I care sufficiently to actually do it. Yeah, it's a... I can't really think of anything particularly useful to go with this right now, but it's an extremely interesting prototype, and I was surprised at just how little work it was. 400 lines of TypeScript for the kernel. The bulk of the work it was actually trying to figure out how the JSX stuff worked. I did start out using Preact for the UI layer, but unfortunately some of the rather dodgy things I've done with object construction, it really doesn't get on well with Preact, so I had to give up on that. It only supports a subset of the TypeScript syntax. For example, each quote, file, unquote, has to contain a single class level element. So if you try and create a new one, then it will reject that. The reason for this is for the hot patching to work. We have to create a proxy for every class. It's just a box into which we can add methods, and of course, you have to tell it the name of the class so it can create the proxy. You can't do things like static properties because loading each class cannot depend on code in any other class because we don't know the order in which they're loaded, and this involves actually executing code at load time to evaluate the value of the variable. You can have static properties, you just can't have initializers on them. I've already mentioned the type problems involved in adding instance methods to a running class. You can't really get around that. The biggest issue with turning this into a real development environment is there is no debugger. In order to do debug stuff, I have to pull up the Chrome debugger and do it from there. This is not actually too bad. You can see the rewritten class, but it'd be really nice to be able to integrate the debugger into the editor here, but I can't really see any way of doing that. It would be possible to use a JavaScript to JavaScript compiler and run individual classes, essentially in a little sandbox. Yes, this is doing that red thing again. Bizarrely, that is not a syntax error. I believe this is actually just created a instance variable called DFS. That's a syntax error, and now it goes white. Oh, yes, JavaScript to JavaScript translators. It would be possible to take the code for a given class and wrap it so that each method was interpreted in a embedded JavaScript interpreter, which would allow debugging through that class, and we could patch in and out individual methods from either the native implementation and the wrapped implementation in the interpreter. I don't really want to because it sounds really nasty, but that would probably allow the ability to debug stuff. Yes, there's stuff to do with this. I'm not entirely sure whether I'm actually going to continue on, but it does show that this style of programming is not completely dead. Even today, we have systems in place that will do it fine. Yeah, I hope this was interesting. I will be putting this environment up on cowlark.com at some point for people to play with. Let me know in the comments what you think.