What We All Pretend to Know: The Differences Between the JS Engine & JS Runtime

This ad is not shown to multipass and full ticket holders
JSNation US
JSNation US 2025
November 17 - 20, 2025
New York, US & Online
See JS stars in the US biggest planetarium
Learn More
In partnership with Focus Reactive
Upcoming event
JSNation US 2025
JSNation US 2025
November 17 - 20, 2025. New York, US & Online
Learn more
Bookmark
Rate this content

Modern web development frameworks provide so many levels of abstraction that developers don’t need a deep understanding of the underlying runtimes or engines to hit the ground running. In fact, ease of use is the point of modern frameworks and a good indicator of their success. When spinning up an application, it’s easy to view Node as just “a piece of the puzzle,” and mistake it for a JavaScript engine, or a language, when, in reality, it is neither.

It is valuable for developers to understand the differences between the engines and runtimes employed in their full-stack projects in order to have a mental model of how their code is being executed, and to optimize it for the environments in which it is running. The goal of this talk is to make clear the distinction between a JavaScript engine and runtime, to review how they interact with one another (in both servers and on the web), and to look at practical applications of this knowledge.

This talk has been presented at JSNation 2025, check out the latest edition of this JavaScript Conference.

FAQ

ECMAScript defines the syntax, available types, operators, and language semantics of JavaScript. It acts as the specification outlining the rules for JavaScript code.

JavaScript engines parse the source code into an abstract syntax tree, which is then executed by the interpreter as byte code. The engine may optimize frequently executed code paths by compiling them into machine code using profiling data.

Common JavaScript runtimes include Node.js, Browser, Deno, Bun, and Electron, while popular engines are V8 and JavaScript Core.

The event loop monitors the call stack and queues (microtask and task queues) to decide when code is executed, allowing JavaScript to behave asynchronously despite being single-threaded.

Inline caches help JavaScript engines by remembering the structure of functions, enabling faster execution through type stability and consistent object shapes.

A JavaScript engine is a low-level program that parses and executes JavaScript code, converting it into machine code. A JavaScript runtime, on the other hand, embeds the engine and adds APIs for the specific environment, managing the event loop and enabling code execution.

APIs in JavaScript runtimes allow the code to interact with the environment, providing functionalities specific to running JavaScript on a server (Node.js) or in a browser.

Understanding the JavaScript engine and runtime helps developers build a mental model of how their code is executed, allowing them to write and debug JavaScript code more effectively.

Karina Ionkina
Karina Ionkina
Samiul Huque
Samiul Huque
11 min
16 Jun, 2025

Comments

Sign in or register to post your comment.
Video Summary and Transcription
The talk delves into the intricacies of JavaScript engine and runtime, emphasizing the importance of understanding execution processes for effective development and debugging. It discusses how JavaScript engines optimize code through parsing, abstract syntax trees, and byte code execution. Strategies for optimizing code include maintaining type stability, using type-stable arrays, and consistent object shapes to avoid deoptimization. Recommendations for enhancing JavaScript execution involve ensuring input type consistency, using type-stable arrays, and understanding the event loop's role in code execution across different runtimes and engines.

1. Introduction to JavaScript Engine and Runtime

Short description:

The talk introduces the difference between the JavaScript engine and runtime, emphasizing the importance of understanding the execution process for better code development and debugging.

Hi everyone, welcome to our talk. Today we're going to talk about what we all present to know, the difference between the JavaScript engine and the JavaScript runtime. Before we begin, my name is Sam and this is my co-speaker, Karina. We are both from New York, we both play tennis, we both went to the same high school, and we both currently work at Bloomberg as software engineers. And today we're both really excited to talk to you about JavaScript. Before we dive in, why are we giving this talk and why is it relevant to you? Modern JavaScript development is full of very useful frameworks that let you build and ship code really quickly. But these frameworks abstract away a lot of the low-level intricacies that make your code run. We think understanding what's going on under the hood will allow you to have a mental model of how your code is being executed so you can more easily write and debug your JavaScript code. JavaScript is technically a specification, that's all it is. ECMAScript is the name of the specification for JavaScript and it defines what JavaScript is. So what exactly does ECMAScript define? It defines the syntax, the available types, the operators, and language semantics. You can think of these as the rules for the characters that you actually type, like for, while, if, brackets, and so on. In addition to ECMAScript, there's also the engine which Karina will talk about, and that engine implements the specification. And then there's the runtime that takes the engine and allows you to run your code. And now I'll hand it over to Karina.

2. Exploring JavaScript Engine Optimization

Short description:

JavaScript engines are low-level programs that parse and execute code, converting it into fast machine code for CPU execution. The components process JavaScript by parsing the source code, creating an abstract syntax tree, and executing byte code. Interpreters gather type information, profiling data, and optimize code based on past behaviors.

Yeah, so let's take a closer look at the engine. The MDN web docs have a definition for JavaScript engines. Simply put, they're just low-level programs that parse and execute JavaScript code. They also convert your high-level JavaScript into fast machine code that can be executed by the CPU for some code paths. And we'll go over how it does this by first taking a look at how the components of the engine process the JavaScript code.

So first, your JavaScript source code is passed into the parser. It parses that source code and turns it into a tree representation of the source code or the abstract syntax tree. Then that tree is passed into the interpreter which reads and executes engine-specific low-level instructions called byte code. JavaScript is a dynamic language, so types aren't known until runtime, and that makes it hard to optimize in advance, unlike languages like C or C++.

JavaScript interpreters do a couple of things. One, they execute the byte code step by step. But as they do that, they also gather type information and profiling data, which includes information like function call frequency, argument shapes, and when a function is called frequently, which we call a hot function or a hot code path, the engine identifies it as worth optimizing. As the optimizing compiler optimizes your code, it relies on assumptions based on how your code has behaved so far.

3. Strategies for Optimizing JavaScript Code

Short description:

The compiler creates optimized machine code based on data types and code behaviors. Developers can ensure type stability, use type-stable arrays, and maintain consistent object shapes. Optimizations rely on assumptions, and breaking them leads to deoptimization, affecting performance.

And the compiler will use that information to create highly optimized machine code to be executed by the CPU directly. And there are a few methods that the compiler employs to generate this fast machine code. It uses type specialization, which is basically generating machine code tailored to the actual data types used at runtime. It uses function inlining, where it replaces small functions with their actual bodies on the call site, and also loop unrolling.

As the optimizing compiler optimizes your code, it relies on assumptions based on how your code has behaved so far. So it might see that a variable is always a number, or it might notice that an object always has the same structure, and these help the engine generate that highly efficient machine code and skip runtime checks. However, if an assumption breaks, that optimized machine code is discarded and the original byte code is sent back to the interpreter. So this can be time-consuming, and frequent deoptimizations due to unstable code patterns can add significant overhead.

There are a couple of things that we as developers can do to prevent the assumptions from breaking. One assumption that your engine makes is that variables and function parameters are consistently used with the same types. So we can ensure type stability by keeping our functions monomorphic, and that helps your engines use inline caches to remember the structure of the function. Another example is using type-stable arrays, or arrays where all of the elements are of the same type, for example, numbers, and that allows the engine to use special internal representations that are broken when you introduce a mixed type.

4. Optimizing JavaScript Execution and Runtimes

Short description:

Ensure consistent input types, use type-stable arrays, maintain object shapes, and avoid megamorphic call sites. Keep functions pure, short, and type stable for optimization. Understand the runtime, its role in code execution, and the significance of APIs for different environments.

So we want to make sure that we always use the same types of input. Another example is using type-stable arrays, or arrays where all of the elements are of the same type, for example, numbers, and that allows the engine to use special internal representations that are broken when you introduce a mixed type. The engine also loves consistent object shapes. It assumes that they follow the same internal structure and create hidden classes based on things like property names and property order. So we can avoid adding properties after object construction. Adding an attribute alone doesn't guarantee that it will break the assumptions, it largely depends on the context and whether it changes the hidden class shape, but as a general rule of thumb, you want to keep object shapes consistent. You also want to avoid megamorphic call sites, which is function calls or property access that uses different types or shapes at runtime. We want to keep it consistent because optimizers can't inline a function if it's unclear which version is being called. And the engine also loves it when hot-code functions are predictable. We can help make them predictable by avoiding function declarations in loops to reduce scope complexity. Creating closures repeatedly makes the just-in-time compiler treat them as new every time, and that breaks optimization. We can also use pure, short, and inlinable functions. So avoid side effects and avoid relying on or changing anything outside of the function scope to keep the functions pure. So in short, keep your functions pure, short, and type stable, and use tools such as the node trace opt flag to monitor what is being optimized and deoptimized to improve your performance. And that way you'll keep your engine happy and help it to optimize your code.

Now I'll pass it back to Sam. Thanks, Karina. So let's go into the runtime a little bit. What is the runtime? The runtime The runtime is just code. Just like the engine is low-level code that Karina just talked about, the runtime is also low-level code. It's code that does three main things. It invents the engine, it adds extra API calls for the specific environment that you're running in, and then it manages the event loop. So on the right, we have a made-up example of a runtime that we're going to go over in each of the steps right now. But before we begin, what are some widely used runtimes? The two most popular ones are Node, which is what you use to program JavaScript on the back end, and then there's Browser, which we're all familiar with. Every browser that you've probably used, Chrome, Firefox, Safari, has its own unique JavaScript runtime. So what does it mean for a runtime to embed the engine? If we take Node as an example, Node is a runtime that's implemented in C++, and you can see on the right that it is importing something called V8. V8 is the engine that the Node runtime uses, and V8 exposes an API for embedding. So if you were to make your own runtime with V8 as the engine, you could import that and then instantiate it, and now you have that as the engine for your runtime. And the runtime also adds APIs. Why do we need APIs in a runtime? We need them because JavaScript behaves very differently in the different environments that it runs on.

5. Understanding JavaScript Runtimes and Event Loop

Short description:

Explain Node and browser runtime differences, common APIs like FS and DOM, varied runtimes and engines. Understand the event loop, its components, and how it decides code execution. Recap the engine and runtime as low-level code for JavaScript execution.

When JavaScript is running in Node on a back-end server, it needs to do very different things than what JavaScript would need to do if it's running on the browser on your computer, free to access a website. In order to do this, that's when the runtime implements these APIs, and to do that, in your low-level code, you could define a function, and then after that, you would do two things. You would define a binding, which allows you to expose that function that you write in C++, or C, or another low-level language, and then two, after you define the binding, you can then call it in JavaScript. So we talked about Node in the browser. What are some common runtime APIs for Node? In Node, you want to be able to access the file system with FS, you want to get system-level info like CPU or memory info, you can do that with the OS API, and then you also want to create services using HTTP and HTTPS. If you are using JavaScript on a browser, and you're on a browser runtime, you want to do very different things. You want to be able to interact with HTML via the DOM, you want to make network requests using Fetch, and then you want to store things in your browser using local storage and session storage. There are a lot of other runtimes and engines. This is not a complete list, but a few runtimes and engines you should know about. There's Deno, Bun, and Electron, those are runtimes, and then for engines, there's V8, there's JavaScript Core, and then there's V8 Plus Chromium.

So far, we've covered the two things that the runtime does. The third most important thing, not the most important thing, but a very important thing, is the event loop. What is the event loop? Well, the runtime used the event loop to determine when to run your code. The event loop is just additional code that's part of the runtime that you write, and some event loops are even libraries. Like Node uses the liduv library of the event loop, which is a C++ library that you can embed in your own code. In order to understand what the event loop is doing, there are four components that we want to keep in mind. There's the actual event loop itself, which is the code that's written in the runtime. There's a call stack, which is where your code is actually executed, and then there are two queues, the microtask queue and the task queue, that is also referred to as the macro task queue. And these all combine with the runtime, the engine, and all the other components to execute your code. So on the right we have pseudocode that's showing what happens at a high level. What's happening is that the event loop checks to see if the call stack is empty. If it is, it takes everything from the microtask queue and places that onto the call stack for that code to be executed. And then once all of the items from the microtask queue are done, it takes one item from the task queue or the macro task queue, puts that into the call stack to be executed. So the event loop is a continuously running process you can think of that monitors the call stack and the queue to decide when code is executed. And this is the primary way JavaScript behaves asynchronously, even though it is single-threaded.

To recap, the engine is a low-level code that executes JavaScript. Similarly to the engine, the runtime is also low-level code and it allows your code to be run. Thank you for listening to our talk.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

It's a Jungle Out There: What's Really Going on Inside Your Node_Modules Folder
Node Congress 2022Node Congress 2022
26 min
It's a Jungle Out There: What's Really Going on Inside Your Node_Modules Folder
Top Content
The talk discusses the importance of supply chain security in the open source ecosystem, highlighting the risks of relying on open source code without proper code review. It explores the trend of supply chain attacks and the need for a new approach to detect and block malicious dependencies. The talk also introduces Socket, a tool that assesses the security of packages and provides automation and analysis to protect against malware and supply chain attacks. It emphasizes the need to prioritize security in software development and offers insights into potential solutions such as realms and Deno's command line flags.
ESM Loaders: Enhancing Module Loading in Node.js
JSNation 2023JSNation 2023
22 min
ESM Loaders: Enhancing Module Loading in Node.js
Top Content
ESM Loaders enhance module loading in Node.js by resolving URLs and reading files from the disk. Module loaders can override modules and change how they are found. Enhancing the loading phase involves loading directly from HTTP and loading TypeScript code without building it. The loader in the module URL handles URL resolution and uses fetch to fetch the source code. Loaders can be chained together to load from different sources, transform source code, and resolve URLs differently. The future of module loading enhancements is promising and simple to use.
Towards a Standard Library for JavaScript Runtimes
Node Congress 2022Node Congress 2022
34 min
Towards a Standard Library for JavaScript Runtimes
Top Content
There is a need for a standard library of APIs for JavaScript runtimes, as there are currently multiple ways to perform fundamental tasks like base64 encoding. JavaScript runtimes have historically lacked a standard library, causing friction and difficulty for developers. The idea of a small core has both benefits and drawbacks, with some runtimes abusing it to limit innovation. There is a misalignment between Node and web browsers in terms of functionality and API standards. The proposal is to involve browser developers in conversations about API standardization and to create a common standard library for JavaScript runtimes.
How Bun Makes Building React Apps Simpler & Faster
React Day Berlin 2022React Day Berlin 2022
9 min
How Bun Makes Building React Apps Simpler & Faster
BUN is a modern all-in-one JavaScript runtime environment that achieves new levels of performance. It includes BUN dev, a fast front-end dev server, BUN install, a speedy package manager, and BUN run, a fast package runner. BUN supports JSX, has optimized React server-side rendering, and offers hot module reloading on the server. The priorities for BUN include stability, node compatibility, documentation improvement, missing features in BUN install, AST plugin API, native Windows support, Bundler and Minifier optimization, and easier deployment to production. BUN's AST plugin API allows for bundle-time JavaScript execution and embedding code, potentially inspiring new frameworks.
Out of the Box Node.js Diagnostics
Node Congress 2022Node Congress 2022
34 min
Out of the Box Node.js Diagnostics
This talk covers various techniques for getting diagnostics information out of Node.js, including debugging with environment variables, handling warnings and deprecations, tracing uncaught exceptions and process exit, using the v8 inspector and dev tools, and generating diagnostic reports. The speaker also mentions areas for improvement in Node.js diagnostics and provides resources for learning and contributing. Additionally, the responsibilities of the Technical Steering Committee in the TS community are discussed.
Node.js Compatibility in Deno
Node Congress 2022Node Congress 2022
34 min
Node.js Compatibility in Deno
Deno aims to provide Node.js compatibility to make migration smoother and easier. While Deno can run apps and libraries offered for Node.js, not all are supported yet. There are trade-offs to consider, such as incompatible APIs and a less ideal developer experience. Deno is working on improving compatibility and the transition process. Efforts include porting Node.js modules, exploring a superset approach, and transparent package installation from npm.

Workshops on related topic

Node.js Masterclass
Node Congress 2023Node Congress 2023
109 min
Node.js Masterclass
Top Content
Workshop
Matteo Collina
Matteo Collina
Have you ever struggled with designing and structuring your Node.js applications? Building applications that are well organised, testable and extendable is not always easy. It can often turn out to be a lot more complicated than you expect it to be. In this live event Matteo will show you how he builds Node.js applications from scratch. You’ll learn how he approaches application design, and the philosophies that he applies to create modular, maintainable and effective applications.

Level: intermediate
Build and Deploy a Backend With Fastify & Platformatic
JSNation 2023JSNation 2023
104 min
Build and Deploy a Backend With Fastify & Platformatic
Top Content
WorkshopFree
Matteo Collina
Matteo Collina
Platformatic allows you to rapidly develop GraphQL and REST APIs with minimal effort. The best part is that it also allows you to unleash the full potential of Node.js and Fastify whenever you need to. You can fully customise a Platformatic application by writing your own additional features and plugins. In the workshop, we’ll cover both our Open Source modules and our Cloud offering:- Platformatic OSS (open-source software) — Tools and libraries for rapidly building robust applications with Node.js (https://oss.platformatic.dev/).- Platformatic Cloud (currently in beta) — Our hosting platform that includes features such as preview apps, built-in metrics and integration with your Git flow (https://platformatic.dev/). 
In this workshop you'll learn how to develop APIs with Fastify and deploy them to the Platformatic Cloud.
Building a Hyper Fast Web Server with Deno
JSNation Live 2021JSNation Live 2021
156 min
Building a Hyper Fast Web Server with Deno
Workshop
Matt Landers
Will Johnston
2 authors
Deno 1.9 introduced a new web server API that takes advantage of Hyper, a fast and correct HTTP implementation for Rust. Using this API instead of the std/http implementation increases performance and provides support for HTTP2. In this workshop, learn how to create a web server utilizing Hyper under the hood and boost the performance for your web apps.
0 to Auth in an Hour Using NodeJS SDK
Node Congress 2023Node Congress 2023
63 min
0 to Auth in an Hour Using NodeJS SDK
WorkshopFree
Asaf Shen
Asaf Shen
Passwordless authentication may seem complex, but it is simple to add it to any app using the right tool.
We will enhance a full-stack JS application (Node.JS backend + React frontend) to authenticate users with OAuth (social login) and One Time Passwords (email), including:- User authentication - Managing user interactions, returning session / refresh JWTs- Session management and validation - Storing the session for subsequent client requests, validating / refreshing sessions
At the end of the workshop, we will also touch on another approach to code authentication using frontend Descope Flows (drag-and-drop workflows), while keeping only session validation in the backend. With this, we will also show how easy it is to enable biometrics and other passwordless authentication methods.
Table of contents- A quick intro to core authentication concepts- Coding- Why passwordless matters
Prerequisites- IDE for your choice- Node 18 or higher
Build Peer-to-Peer Applications with Pear Runtime
JSNation 2024JSNation 2024
152 min
Build Peer-to-Peer Applications with Pear Runtime
WorkshopFree
David Mark Clements
David Mark Clements
Learn how to rapidly build peer-to-peer applications with Pear Runtime. No servers required. Understand peer-to-peer paradigms and construct applications from well-defined building blocks. This workshop will cover how to create both Desktop and Terminal applications (with discussion for Mobile) that work entirely peer-to-peer from anywhere in the world. By the end of this workshop you should know how to build a new type of highly scalable application with entirely reduced infrastructural costs (~0) along with suitable architectures and best practices for peer-to-peer applications. From the creator of Pear Runtime and the company that brings us keet.io. Table of content:- Introducing Pear- Initial Q & A- Getting Setup- Creating a Pear Desktop Application- Sharing a Pear Application- Running a Pear Application- Creating a Pear Terminal Application- Releasing a Pear Application- Architectural Discussions- Wrap-up Q & A
GraphQL - From Zero to Hero in 3 hours
React Summit 2022React Summit 2022
164 min
GraphQL - From Zero to Hero in 3 hours
Workshop
Pawel Sawicki
Pawel Sawicki
How to build a fullstack GraphQL application (Postgres + NestJs + React) in the shortest time possible.
All beginnings are hard. Even harder than choosing the technology is often developing a suitable architecture. Especially when it comes to GraphQL.
In this workshop, you will get a variety of best practices that you would normally have to work through over a number of projects - all in just three hours.
If you've always wanted to participate in a hackathon to get something up and running in the shortest amount of time - then take an active part in this workshop, and participate in the thought processes of the trainer.