Video 01

The cost of JavaScript

11:13

The thumbnail of the video The cost of JavaScriptWatch on YouTube (opens in a new tab)

Watch on YouTube (opens in a new tab)

Intro

People reach for JavaScript to build interfaces because it's easy and you get something on screen fast. And that's a completely fair thing to want.

So the bet with RayClay is that if you make C easy enough for that job, and you can write the interface in roughly the same number of lines, it becomes competitive with the JavaScript ease of development.

And of course, if the developer experience is similar between C and JavaScript, then C becomes the obvious choice because it runs circles around JavaScript when it comes to performance and resource usage.

But you just don't see modern web-like interfaces outside of JavaScript frameworks. When you look at desktop apps built in lower level languages, they often lack those nice rounded buttons and vibrant colours and they ultimately look and feel outdated.

So developers and companies building desktop apps became hypnotised by JavaScript frameworks like Electron because they can deliver that modern browser-like experience to their users. And all they had to do was package the entirety of Chromium into their apps.

But here are some examples of desktop apps I've built with RayClay. These apps are written in pure C, but they look and feel just like what you'd see in a modern browser. So it is totally possible to deliver that same user experience in a far more performant language.

And that's the crux of what I mean when I say a JavaScript desktop app is borderline software pollution. I'm just kidding, but like seriously; some of these JavaScript apps are crazy bloated.

In fact, the memory price for the latest MacBook Pro is $25 per gigabyte, and most of these apps are using like half a gig of memory. They really are living rent free on the hardware real estate that you paid a lot of money for.

And unfortunately, nowadays when consumers find their computer is getting slow, they often blame the computer itself when it could just as well be the bounty of apps-pretending-to-be-browsers that they have downloaded over the years, which are draining your battery life, making your fans spin, and overall just degrading your experience on your computer.

And that is the cost of JavaScript. It is fundamentally resource intensive. If you want to write a self-contained desktop app with the unholy trinity JavaScript, HTML, and CSS, you're looking at a minimum install size of 200 megabytes and double that for your memory usage at runtime. You don't need all this stuff to render some pixels on your user's computer. Like actually - you don't. You can build the exact same app, deliver the exact same UI, and exact same capability, at a fraction of the resource usage, by just using a lower level programming language. In fact, Chromium, the backbone of your favourite JavaScript desktop app, is written in C++ because... well actually I don't really want to think about what it would look like if they tried to write Chromium in JavaScript.

And if you're vibe coding, yes I'm talking to you, then what's even the point of picking a JavaScript framework? All the difficulty is being abstracted away from you anyway. And your favourite AI model has read every C textbook in existence. In fact, here's an example of how much lighter RayClay is compared to some other common frameworks.

You might notice some of these JavaScript frameworks ship way lighter than Electron but then borrow a massive amount from the end-user's machine. That's because, rather than bundling their own browser, these frameworks use the native web view that you already have on your computer. This makes their size much smaller, but they have a massive Achilles heel: you have no control over what web view version your end user has or, in other words, your app is not self-contained; it relies on external stuff and fingers-crossed your user has the right stuff on their computer otherwise your app might randomly break. And it's a constant cat-and-mouse game where these frameworks have to update every time Apple or Microsoft update their web views and threaten to rip apart your UI. Basically, a self-contained app is just way easier to maintain. And that's a large reason why Electron is still so popular.

But anyway, aside from all that, these are still JavaScript frameworks so they aren't addressing the fundamental issue: that they require a browser (or web view) in the first place. And so RayClay still wins out by miles, all while being self-contained too.

And whether you're a mature company already distributing an app to millions of users or you're smaller and on a tight budget, I'd say the moment for switching is better than it's ever been because re-writing an interface into a different language used to be an incredibly expensive procedure and it really isn't anymore. If you've got something that works, an app that people already find valuable, but you've been putting off getting it off a JavaScript framework such as Electron, the job is a lot smaller today than it was even just 2 years ago.

But there's still that one nagging thing, writing HTML, CSS and JavaScript is just so much easier than writing in a language like C. Although I think that argument is basically redundant with the progress of AI, large companies still have a thousand front-end web developers and they still need to put them to work on something. And this was at the forefront of my mind when I was developing RayClay, you are still writing in C but I think that the syntax will look quite familiar for front-end web devs.

So let's actually see what developing with RayClay looks like.

Coding

So I'll start by setting a RayClay project up from complete scratch. Clone the repo, write CMakeLists.txt that adds Rayclay as a subdirectory and link it to your target and that's the CMake config done. Then to open your first window it's literally two lines in main.c. The first build takes a bit longer because it's compiling the window layer as well and everything after this one is incremental. And there you go, a completely cross platform window. Pretty cool. And about as simple as it gets.

git clone https://github.com/impizulu/rayclay.git
Build 1 - clone RayClay
cmake_minimum_required(VERSION 3.21)
project(app C)
set(CMAKE_C_STANDARD 99)

add_subdirectory(rayclay)
add_executable(app main.c)

target_link_libraries(app PRIVATE rayclay)
Build 1 - CMakeLists.txt
#include "rayclay.h"

int main(void) { return rcRunApp(NULL); }
Build 1 - main.c
cmake -B build
cmake --build build
build/app
Build 1 - build and run

That's not my app in there by the way, it's a welcome screen because I haven't written a layout yet. It says exactly that in the terminal and it names the field to set to replace it.

And you can see I'm playing around with the zoom. Out of the box RayClay has that browser-like layout zoom. I was being serious when I said I want the experience to be just like developing on top of a browser. Of course you can turn it off if you like but later I'll show you the panning and canvas-like zoom we have with RayClay. It's pretty cool how easy it is to set up.

So now I'm moving in an assets folder containing my app icon and we can immediately start setting some app options. There's one option struct, I fill it in and pass a pointer to it instead of null and I set a width, a height, a title and the logo for the application. Build it, run it and there's the custom icon, the custom title, and the window opening at the dimensions I asked for.

mv ~/Downloads/assets .
Build 2 - move in the app icon
#include "rayclay.h"

int main(void) {
    RC_AppOptions opts = {
        .width = 1400,
        .height = 1250,
        .title = "RayClay Tutorial App",
        .iconPath = "assets/icons/app-icon.png",  // PNG / JPG / BMP
    };

    return rcRunApp(&opts);
}
Build 2 - main.c
cmake --build build
build/app
Build 2 - build and run

Now let's start getting properly custom. I set nativeFrame to true and the title bar your operating system hands you is gone - instead RayClay is now drawing its own with the title and the minimize, maximize and close buttons already on it out of the box and entirely cross-platform. We have officially entered fully custom territory.

#include "rayclay.h"

int main(void) {
    RC_AppOptions opts = {
        .width = 1400,
        .height = 1250,
        .title = "RayClay Tutorial App",
        .iconPath = "assets/icons/app-icon.png",  // PNG / JPG / BMP

        .nativeFrame = true,
        .titlebarHeight = 48,
    };

    return rcRunApp(&opts);
}
Build 3 - main.c
cmake --build build
build/app
Build 3 - build and run

But let's build a title bar of our own. For that I need a layout and I'm going to run it here just to show you what we've got because right now it's genuinely a blank window: no title bar at all, just blank. And that's 20 lines of code; 20 lines and you've got a cross-platform blank canvas to author your application. You have total control over the window. Obviously a black rectangle is useless in practice but it's the clearest way I can show you how much control RayClay actually hands you in minimal lines of code.

#include "rayclay.h"

static void layout(RC_App* app, void* userData) {
    rcColumn(.id = "Root", .w = "grow", .h = "grow", .bg = RC_BLACK) {}
}

int main(void) {
    RC_AppOptions opts = {
        .width = 1400,
        .height = 1250,
        .title = "RayClay Tutorial App",
        .iconPath = "assets/icons/app-icon.png",  // PNG / JPG / BMP

        .nativeFrame = true,
        .titlebar = {.custom = true},
        .layoutCallback = layout,
    };

    return rcRunApp(&opts);
}
Build 4 - main.c
cmake --build build
build/app
Build 4 - build and run

Floor Apps Comparison

And this is a great moment to show you what that empty window costs in other frameworks by comparison. No content - just the absolute bare minimum application that I could ship to an end user. In fact, I produced the bare minimum empty window for 8 frameworks.

On my Linux computer, the Rayclay app floor takes 3 MB of disk space against Electron's 176. Leave it in the background and RayClay holds 15 MB of memory. Electron holds 114. And RayClay does it in one process; Electron needs eight.

These numbers make it pretty obvious just how much bloat is packed into Electron and some of these other frameworks. And I'm calling this the application floor because you can't get any lower - these are the numbers to ship a literal empty window to your user, before you've even added any UI elements or features.

RayClay is even competitive with Qt which is exactly the kind of framework I want to be competitive with - it's written in C++, was released all the way back in the 90s and has had a massive amount of support and decades to mature. And RayClay has a nice leg up on Qt because we are entirely open source whereas Qt requires a commercial license if you want to keep your codebase private and prices vary by tier and team size which I think is a big turn off for large businesses.

I don't really want to bash an open source project, but Electron has over 1,000 contributors, donations from massive companies and over a decade to try and make performance improvements to their framework and in just a couple months I can wipe the floor with them. That's not because I'm a better developer, it's because they're using JavaScript and I'm using C. The difference is very real and very measurable.

Coding Continued...

So now a bit of structure. I'll define a small theme for the application and then write my own title bar and layout. A row tagged is the part that drags the window, a box inside of it for the close button and a check on whether the pointer is over that box so I can paint it red. And there we are, 39 lines, colours included, for a window I have complete control over.

#include "rayclay.h"

#define BG_APP rcRgb(18, 18, 20)
#define BG_PANEL rcRgb(24, 24, 27)
#define BG_HOVER rcRgba(255, 255, 255, 28)
#define DANGER rcHex(0xc42b1c)
#define FG_TEXT rcRgb(288, 288, 231)

static void layout(RC_App* app, void* userData) {
    rcColumn(.id = "Root", .w = "grow", .h = "grow", .bg = BG_APP) {
        // Titlebar: window drag area
        rcRow(.id = RC_ID_WINDOW_DRAG, .w = "grow", .h = "100px",
             .bg = BG_PANEL, .align = "cl") {
            // Titlebar: close button
            bool over = rcIsHovered(RC_ID_WINDOW_CLOSE);
            rcBox(.id = RC_ID_WINDOW_CLOSE, .w = "20px", .h = "100%",
                  .bg = over ? DANGER : BG_HOVER) {}
            // Titlebar: title text
            rcBox(.w = "grow", .align = "cc") {
                rcTextL("RayClay Tutorial App", .color = FG_TEXT);
            }
        }
    }
}

int main(void) {
    RC_AppOptions opts = {
        .width = 1400,
        .height = 1250,
        .title = "RayClay Tutorial App",
        .iconPath = "assets/icons/app-icon.png",  // PNG / JPG / BMP

        .nativeFrame = true,
        .titlebar = {.custom = true},
        .layoutCallback = layout,
    };

    return rcRunApp(&opts);
}
Build 5 - main.c
cmake --build build
build/app
Build 5 - build and run

And the reason I picked the title bar for this demo is that, in my opinion, it's about the hardest part of a cross-platform framework to get right. Windowing behaviour is deeply tied to whichever operating system you're on and the title bar is one of the worst parts of it, which is why some frameworks just hand you the system one and leave it there. So when a framework gives you the title bar that tells you how much of the window it's really giving you.

Now having shown you all of that I wouldn't actually set up a project this way. What I do is start from the RayClay template because it does all of this out of the box and then you customize the title bar however you like. So I'll delete everything here and pull that down instead. And there's a clean application straight away and this one's structured properly. It's split into source files, a theme where you can define your color scheme, then the title bar and then a sidebar and the content area which you can keep or delete or replace or do whatever you want. It's a much nicer way to structure your application so the code stays maintainable and it's the thing I'd hand you if you asked me where to start.

rm -rf *

git clone https://github.com/impizulu/rayclay-template.git .

cmake -B build
cmake --build build
build/app
Recommended - start from the template instead

And as I promised earlier, here's a quick demo of the panning and canvas-like zoom which I've put into the RayClay template since it's really just a few extra lines of code.

Outro

So why would you actually switch from a JavaScript framework to RayClay? The obvious answer is your users, but I think the more interesting one is that everyone in the chain does better out of it:

  • You, the app provider, become more competitive because now you are shipping the most performant application in your category, that's a measureable competitive advantage you can actually point at. Because your users can feel a 10x performance difference.
  • But also hardware and OS providers like Apple and Microsoft benefit because a slow computer looks like their fault when it might actually be the apps running on it. So they look better too when developers ship more performant apps. And in fact, they are already clamping down on the flood of low-effort apps arriving in their app stores.

This video is also about a bit more than RayClay. If you haven't gathered already, I am getting a bit frustrated at the amount of bloated JavaScript applications creeping onto the desktop ecosystem. The issue is also just as bad on mobile; I actually have future plans to expand RayClay to support mobile. But this is the first public release, so I want to see how much support I get for the project before committing more effort to it.

So half of the message of this video is really about getting desktop developers to switch away from JavaScript frameworks. And the other half is showing you that RayClay is a solution out there for you to check out.

So, to close out the video RayClay's in beta - this is the first ever release so it will only get more feature rich and more stable from here - and it's on GitHub, along with the template. It's C99, it builds with CMake, you can ship a self contained app, is has frontend web dev like syntax, its way more efficient than any Javascript app you could ever build, obviously - it's in C, and of course its totally open source and free to use.

All videos