JavaScript
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
239: Introduction to TypedArrays
A few years back, I was mentoring a developer who was trying to build a custom image filter in the browser. He had loaded a raw image file as a massive standard JavaScript array—basically a list of millions of integers representing RGBA values. The result? The browser tab would freeze for three seconds every time he tried to apply a blur, and the memory usage was astronomical. When I looked at his code, I saw he was using a standard [] array. The problem is that standard JS arrays are objects; they're flexible and convenient, but they aren't stored as contiguous blocks of memory. For millions of pixels, that overhead is a performance killer.
This is exactly why TypedArrays exist. When you're dealing with binary data—be it image pixels, audio samples, or network packets—you don't want the "magic" of a standard JavaScript array. You want a raw slab of memory that the CPU can chew through efficiently.
The Slab and the Lens
To understand TypedArrays, you first have to understand the ArrayBuffer. Think of an ArrayBuffer as a raw slab of memory. It's just a sequence of bytes. The catch is that you can't actually manipulate the data inside an ArrayBuffer directly. If you try to access an index on a buffer, you'll get nothing.
To actually read or write to that memory, you need a "View." This is where the TypedArray comes in. A TypedArray is essentially a lens you place over that raw buffer to tell JavaScript how to interpret the bits. Do those 8 bits represent an unsigned integer from 0 to 255? Or does a group of 32 bits represent a floating-point number? You decide by which view you use.
// Create a buffer of 16 bytes
const buffer = new ArrayBuffer(16);
// Create a view that treats the buffer as 8-bit unsigned integers
const uint8View = new Uint8Array(buffer);
// Now we can actually put data in it
uint8View[0] = 255;
uint8View[1] = 128;
console.log(uint8View); // Uint8Array(16) [255, 128, 0, 0, ...]
Choosing Your Precision
Depending on what you're building, you'll pick different TypedArray constructors. I usually categorize them by what I'm trying to achieve. If I'm working with raw file data or colors, Uint8Array (unsigned 8-bit) is the gold standard because it maps perfectly to a single byte.
If I'm doing heavy math or working with WebGL coordinates, I'll reach for Float32Array. If I need to handle larger integers that might be negative, Int32Array is the way to go. The crucial thing to remember is that these arrays are fixed-length. You can't push() or pop() elements like you do with regular arrays. Once you allocate that memory, you're locked in. If you need more space, you have to create a new, larger buffer and copy the data over.
// Let's look at how the same buffer changes based on the view
const sharedBuffer = new ArrayBuffer(4); // 4 bytes total
const view8 = new Uint8Array(sharedBuffer);
const view32 = new Uint32Array(sharedBuffer);
view8[0] = 1;
view8[1] = 0;
view8[2] = 0;
view8[3] = 0;
// Because view32 sees all 4 bytes as one single number,
// it reads the combined value of those 4 bytes.
console.log(view32[0]); // 1
It's a bit of a mental shift. You stop thinking about "lists of things" and start thinking about "how many bytes does this piece of data actually take up?" It feels lower-level, almost like C, but that's exactly why it's so fast.
📋 Practical Task
Building a Simple RGB-to-Grayscale Buffer Processor
In this exercise, you will simulate a basic image processing task. You'll start with a Uint8Array representing a small 2x2 pixel image (where each pixel has 3 values: Red, Green, and Blue). Your goal is to create a new Uint8Array that converts these colors to grayscale by averaging the RGB values for each pixel.
Requirements:
- Initialize an
ArrayBufferof 12 bytes (2x2 pixels * 3 channels). - Wrap it in a
Uint8Arrayand fill it with some sample RGB values (e.g., [255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255]). - Create a second
Uint8Arrayof the same size to hold the grayscale result. - Loop through the original array in increments of 3, calculate the average of the R, G, and B values, and assign that average to all three channels in the result array.
- Log the final grayscale buffer to the console.
There are no comments for now.