Showing posts with label Javascript. Show all posts
Showing posts with label Javascript. Show all posts

Tuesday, December 31, 2024

What are WebSockets, and How Does Socket.IO work?

 You're curious enough to search how a thing that you're using is actually working, instead of blindly integrating it into your code, congrats. But really, what is WebSockets, how does it work, and what is socket io precisely?

What are WebSockets?

I suggest you get to know how HTTP servers work before continuing on this topic. Anyways, Websockets are two-way TCP connections that are mainly helped by a long-living HTTP server, It is extremely useful for apps that require real-time interaction and activity like chat apps, notifications, collaborative tools, you name it, it's a wide topic to discover.

Some technique in HTTP was used before WebSockets were invented, one of them was HTTP polling, it's the action of waiting a couple seconds before asking the server if it has new data, this method is obviously ineffective and will waste resources as you go.

Other methods used:

  1. AJAX (Asynchronous JavaScript and XML): This technology allows web applications to make asynchronous requests to the server without reloading the entire page. Google was among the early adopters of AJAX in the mid-2000s, using it for products like Google Suggest, Gmail, and Google Maps.
  2. Comet: This model, made famous by organizations like Google and Meebo, used long-held HTTP connections to push data from the server to the client. Google initially used Comet to add web-based chat to Gmail, while Meebo used it for its web-based chat app.
  3. Long polling: An improvement over regular polling, where the server holds the connection open until new data is available.
  4. Forever frame: This method establishes a long-lived HTTP connection to the server using a hidden frame.
  5. Java applets with LiveConnect: This early solution allowed for server push by creating a long-held connection with the server and communicating with JavaScript on the web page.
Those weren't actually good solutions, instead, they were hacks, and developers needed better solutions to better the user experience, which led to the discovery of WebSockets.

HTTP vs WebSocket

What is Socket.io and how does it implement WebSockets?

Since we now know what a WebSocket is, let's see how Socket.IO implements it. Socket.IO is an extensive abstraction built on top of the WebSocket protocol (which is a low-level protocol). Understanding the difference between Socket.IO and the WebSocket library is important to grasp how they work.

Key Points:

  1. Socket.IO vs WebSocket:

    • WebSocket is a low-level protocol that handles real-time communication but requires manual handling of various tasks like reconnection, fallback, and event handling.
    • Socket.IO is a high-level library built on top of WebSocket, offering features like automatic reconnection, fallback mechanisms, event-driven messaging, rooms, and namespaces.
  2. How Socket.IO Works:

    • Socket.IO uses WebSocket for the actual communication but adds additional layers to handle things like reconnections, message broadcasting, fallback to HTTP polling if WebSocket is unavailable, and much more.
    • When you use WebSocket, you're directly interacting with the protocol and handling all aspects manually. But Socket.IO abstracts away these complexities to give you a more feature-rich, ready-to-use solution for real-time communication.
  3. When to Use Each:

    • If you want full control and don't need additional features, use WebSocket.
    • If you're focused on getting things done quickly and don't want to handle low-level details like reconnections or fallbacks, use Socket.IO.

In short, Socket.IO provides a higher-level, more user-friendly interface built on WebSocket, making it easier to implement real-time applications without having to deal with manual management of WebSocket connections.

Websocket API on the server
const WebSocket = require('ws'); const server = new WebSocket.Server({ port: 8080 }); server.on('connection', (socket) => { socket.on('message', (message) => { console.log('Received:', message); socket.send('Hello Client!'); }); });
Websocket API on the client
const socket = new WebSocket('ws://localhost:8080'); socket.onmessage = (event) => console.log('Message:', event.data); socket.send('Hello Server!');
Socket.io on the server
const io = require('socket.io')(3000); io.on('connection', (socket) => { socket.on('message', (msg) => { console.log('Received:', msg); socket.emit('message', 'Hello Client!'); }); });
Socket.io on the client
const socket = io('http://localhost:3000'); socket.on('message', (msg) => console.log('Message:', msg)); socket.emit('message', 'Hello Server!');


Wednesday, December 25, 2024

What Are Array Buffers, Uint8Array, and Base64 in Javascript

Binary Data in JavaScript 🖥️

Before diving into this topic, it's important to understand what binary data is and how it relates to creating an image or a file. This overview is not intended to be an in-depth exploration; it's meant to provide you with a basic understanding of these concepts and how they work together.

Working with binary data, like files, images (Read how images are presented), or streams, in JavaScript often involves concepts such as ArrayBuffer, Uint8Array, and Base64. Whether you're new to this or have forgotten these concepts, I'm here to break them down for you.

What is an ArrayBuffer?

ArrayBuffer is not an array of something.

Let’s eliminate a possible source of confusion: ArrayBuffer has nothing in common with an Array.

Key Characteristics of ArrayBuffer:

  1. Fixed Length: The length of an ArrayBuffer is fixed; we cannot increase or decrease it.
  2. Memory Usage: It takes up exactly the specified amount of space in memory.
  3. Accessing Bytes: To access individual bytes of data, we need to use a "view" object, such as a TypedArray. You cannot use buffer[index] directly.

An ArrayBuffer is a fixed-length block of raw binary memory in JavaScript. You can think of it as a "container" for storing a sequence of bytes. However, it doesn’t interact with the data directly—it serves as the base structure for typed arrays, like Uint8Array, that allow you to manipulate the data.

Example:

Creating an arraybuffer example javascript

This code reserves 16 bytes of memory (128 bits) in the memory, which is maintained by JavaScript's garbage collector. You can't resize the buffer, you must create a new one. An ArrayBuffer acts as a memory space, and you manipulate it using typed arrays like Uint8Array.

When to Use an ArrayBuffer?

  • File and Network Operations:
    - Reading files (images, videos).
    - Handling binary protocols (e.g., WebSockets, streams).
  • Data Parsing:
    - Manipulating binary data formats (e.g., .png, .wav files).
    - Decoding/encoding binary protocols.
  • Web APIs:
    - Used with APIs like fetch() to handle raw data (e.g., response.arrayBuffer()).

What is a Uint8Array 

Uint8Array Stands for "Unsigned 8-bit Integer Array". It's a subclass of TypedArray and provides a way to interact with binary data stored in an ArrayBuffer.

How Does It Work?

A Uint8Array allows you to read and write binary data in 8-bit chunks (values between 0 and 255).


Using array buffer with typedArray javascript

Uint8Array is specifically designed to work with binary data in a simpler, more efficient way. It’s commonly used for manipulating binary data like images, audio, and more.

Other Typed Arrays

There are other types of TypedArray classes that handle larger data sizes, such as:

These classes work similarly to Uint8Array but handle different data sizes.

What happens if Uint8Array Overflow

If you try to use a Uint8Array to save an image with a bit depth of 32, it will not be able to directly store the full 32-bit value. The Uint8Array only stores 8-bit values (ranging from 0 to 255).

For example, if a pixel has a value of 4 million (which in 32-bit binary is 00000000001111010000100100000000), and you try to store it in a Uint8Array, the value will be truncated using modulus arithmetic. Specifically, 4 million % 256 equals 0, so the pixel value stored in the Uint8Array would be 0.

This demonstrates that the 32-bit range (which can store much larger values) gets reduced to the 8-bit range of the Uint8Array when the data is stored, losing much of the original information.

Base64: Encoding Binary as Text 🔏

Base64 Is a method for encoding binary data into a string format, making it suitable for text-based protocols like JSON or HTTP. It converts binary data (which can't be easily transmitted via text-based protocols) into an ASCII string.

Why Use Base64?

Base64 is particularly useful for transmitting images or other large binary data as text. For example, an image can be converted into a Base64 string, making it easier to send over the web.

For example, the string "Hello World" in binary:

01001000 01100101 01101100 01101100 01101111 00100000 01010111 01101111 01110010 01101100
01100100

Converts into Base64 as:

SGVsbG8gV29ybGQ=

Base64 encoding increases the size of the data by approximately 33%, but it makes the data compatible with text-based formats. While you wouldn’t encode "Hello World" in a practical scenario, you do need to convert files that consist of binary arrays and array buffers.

Converting Uint8Array to Base64 🔄

You can convert Uint8Array data to a Base64 string using the btoa function:

Converting a Uint8Array to Base64 Javascript

This function converts a Uint8Array to a Base64-encoded string. Where btoa() means binary to ASCII and vice versa with atob()

How These Concepts Work Together 🤝

  1. ArrayBuffer: Stores raw binary data.
  2. Uint8Array: Provides an interface for manipulating that data.
  3. Base64: Encodes the data for storage or transmission in text formats.

Example Workflow:

  1. Download an image and store it in an ArrayBuffer.
  2. Manipulate the image data using a Uint8Array.
  3. Encode it in Base64 to send it via HTTP or embed it in a JSON file.

By understanding these three concepts—ArrayBuffer, Uint8Array, and Base64—you can easily work with binary data in JavaScript, whether you're dealing with file uploads, network protocols, or APIs.

Wrapping Up 🎉

That's it! I know this might be superficial, but this is my first educational post ever, and I hope one of you finds this helpful. Here is a summary of what you have just read:

  1. ArrayBuffer:

    • A fixed-length block of raw binary memory in JavaScript.
    • It doesn't store data directly but serves as a container for binary data that can be manipulated with typed arrays like Uint8Array.
    • Example: new ArrayBuffer(16) Creates a buffer of 16 bytes.
  2. Typed Arrays (Uint8Array):

    • TypedArray is an array that represents a specific numeric type, such as Uint8Array for 8-bit unsigned integers.
    • It interacts with an ArrayBuffer to manipulate the binary data.
    • Example: let arr = new Uint8Array(buffer) Allows modifying data in the buffer.
  3. Base64 Encoding:

    • Converts binary data into a text format, often used for transmitting data like images in text-based protocols like JSON or HTTP.
    • Example: "Hello World" converted to Base64 is SGVsbG8gV29ybGQ=.
  4. Converting Between Formats:

    • btoa() and atob() are used to convert binary data to Base64 (binary-to-ASCII) and Base64 back to binary (ASCII-to-binary).

Example Workflow:

  • Download an image → Store in an ArrayBuffer → Use Uint8Array to manipulate the data → Convert to Base64 for transfer.
For a more comprehensive guide about array buffers visit Javascript.info

How does binary present an image (Raster Graphics)?

Binary code serves as the fundamental language of computers, and developers use it everywhere—in the text you're reading and on the screen you’re looking at. It is an integral part of our digital lives. But how does a code made up of just 0s and 1s create an image? You're in the right place to find out!

At its core, binary consists of 0s and 1s. Each bit can be either a 0 or a 1, and a byte is made up of 8 bits. Because of this, there are 256 possible combinations of 0s and 1s in a byte, calculated by the formula 2^8. This raises the question: how can this sequence of numbers form an image that we can recognize? But first, let’s explore what an image actually is.

What is an image?

An image is just pixels, your graphics card decides how many bits are on one pixel using the bit-depth setting, a bit depth of 8 means that 255 different colors can be represented in that pixel, and so on. 

GrayScal image example using 1-byte or 255 color values
GrayScale image example using 1-byte or 255 color values

This is an example of what's going on, as you can see, the picture is very unclear and that's because it's just 12x16 pixels with a depth of 8. Here is another image to show you how different an 8-bit to a 16-bit can be... we're talking about a difference of almost 63000 other colors, mind-blowing isn't it? We'll continue with the 8-pixel just to keep it simple, you don't wanna see a 5-figure number on a pixel. As a reminder: 8bit-depth means that there is 1 byte in each pixel.

Contrast between 8-bit and 16-bit colors
Contrast between 8-bit and 16-bit colors

That's gotta be all about it to fundamentally understand what an image consists of, but how is it presented?
How is the image presented?
The image, let us say you have an image of resolution 128x128, Which is 128 pixels multiplied by another 128, which is a square, your graphics processor will ask the image, how do I draw you? The image will respond with the bit depth and the byte for each pixel in the 128x128 You can think of it as pixel grid.

What is image resolution:

Resolution is the number of pixels in an image, for example, an image of 128x128 has 16,384 pixels, meaning it has a fixed resolution, unlike a vector that has dynamic resolution no matter how you scale it.

Here is where the color code comes in:
  • RGB (Red, Green, Blue): Each pixel has values for red, green, and blue (e.g., 255, 0, 0 For red).
  • Grayscale: since the grayscale is 1 byte, A grayscale image with the same resolution is one-third the size of an RGB image.
  • RGBA: Same as RGB, but with an extra alpha value for transparency.
  • Other formats like CMYK C for cyan, M for magnate, Y for Yellow, K for Black (used in printing), or grayscale (for black-and-white images).
Example of 1-byte colors
Example of 1-byte colors

There are also image file types called bitmap (Raster Graphics) that use bits to represent the colors, unlike vectors, that use math like SVG and EPS:

JPEG (Joint Photographic Experts Group)

  • Raster, compressed (lossy).
  • Supports 24-bit RGB color (16.7 million colors).
  • No transparency support.
  • Good for photos with smooth color gradients.

PNG (Portable Network Graphics)

  • Raster, compressed (lossless).
  • Supports 8-bit grayscale or 24-bit RGB.
  • Transparency (alpha channel) supported in 32-bit PNG.
  • Good for graphics, icons, and images needing transparency.

BMP (Bitmap)

  • Raster, uncompressed, or minimally compressed.
  • Supports various bit depths: 1-bit, 8-bit, 24-bit.
  • Large file sizes; rarely used now.

GIF (Graphics Interchange Format)

  • Raster, compressed (lossless).
  • 8-bit color palette (256 colors).
  • Supports simple animations and transparency.

TIFF (Tagged Image File Format)

  • Raste supports multiple-bit depths.
  • Often used in high-quality printing and archiving.
  • Lossless compression or uncompressed.

SVG (Scalable Vector Graphics)

  • Vector format, not pixel-based.
  • Can embed raster images (e.g., PNG, JPEG) if needed.
  • Scalable without losing quality.

You might be wondering why we refer to only 255 colors when RGB uses 3 bytes. In reality, developers create RGB using 3 bytes, totaling 24 bits, which allows for nearly 2^24 or 16,777,216 possible colors.

Now, let’s recap: the GPU requests color details and dimensions from an image, and the image provides information such as 128x128. This means the image has a grid of 128 rows and 128 columns. Images store pixel data in a file and the GPU reads and processes this data. Each grid spot (or pixel) contains a color defined by 3 bytes. 

To clarify this further, let's consider a much smaller example: a 2x2 image.

2 x 2 pixels data:
  1. row 1 column 1 (pixel 1): RGB(0,0,0)
  2. row 1 column 2 (pixel 2): RGB(255,0,0)
  3. row 2 column 1 (pixel 3): RGB(0,255,0)
  4. row 2 column 2 (pixel 4): RGB(0,0,255)
these particular RGB values are arbitrary.

What is a bit-depth?

The bit-depth, in a complex level, refers to the amount of data stored in each pixel. Essentially, the higher the bit-depth, the larger the file size, but why is that the case? Let's take an image size of 1280x720 as an example, using a 32-bit depth.

So, what does 32-bit depth mean? At 32 bits, which corresponds to 4 bytes, we have a grid array of 1280x720 pixels. Each pixel consists of 32 bits, which can be interpreted as a sequence of zeros and ones. To calculate the total number of bits for this image, we multiply the width by the height and then by the bit depth: 

1280 pixels x 720 pixels x 32 bits = 29,491,200 bits. 

Since there are 8 bits in a byte, we convert this to bytes by dividing the total bits by 8. This results in:

29,491,200 bits ÷ 8 = 3,686,400 bytes, or approximately 3.68 MB. 

Now, just imagine how different the file size would be if we had only used standard RGB or grayscale instead of RGBA.

It's worth noting that the 4 bytes we refer to typically correspond to RGBA (Red, Green, Blue, Alpha), with the Alpha channel representing opacity.

How to calculate image size

This oversimplifies what happens but you get the idea; RGB doesn't send like this; it actually sends by binary. RGB(255,0,0) represents 111111110000000000000000, which is a bright red color. And obviously, the larger the bit depth the larger the file size, because bytes are the size of everything on your computer, KiloByte, Mega, Tera, since in RGB we have 3 bytes then 1 pixel is equal to 3 bytes of data, there are 128x128 pixels and therefore the image size is 128x128x 3bytes= 48 Kilo Byte or 393,216 Kilo Bit.
Another example of how pixels are affected by bit depth
Another example of how pixels are affected by bit depth


In summary, an image is essentially a data file containing information about its type, resolution, and color representation (bit depth). This data is interpreted by the graphics processor, which determines how each pixel is displayed based on the stored color and resolution details. This seamless process transforms binary data into the visuals we see on our screens, showcasing the power of digital representation.
How the pictures metadata (dimension/bitdepth) are saved
How the pictures metadata (dimension/bit-depth) are saved

This is how the image describes itself: dimensions, bit depth and the dpi.

Moreover, DPI (Dots Per Inch) refers to the number of printed dots contained within one inch of an image printed by a printer. PPI(Pixels Per Inch) refers to the number of pixels contained within one inch of an image displayed on a computer monitor. SONY.

What is PPI

PPI (Pixels Per Inch) plays a key role in how images are displayed on screens. For example, imagine an image with a resolution of 128x128 pixels displayed on a monitor with a resolution of 1920x1080 pixels. If the PPI is set to 1 (hypothetically), the image will appear very small on the screen. However, when you zoom in on the image, it fills more of the screen.

So, how does this work despite the image's small dimensions? When you zoom in, the image is scaled to match your screen's resolution. This means that the number of pixels per inch on the screen increases, causing each image pixel to take up more screen pixels, which can make the image appear larger.

To make the image more visible, the computer scales it, but this process can reduce the image's quality unless it is a vector graphic, which can be scaled without losing resolution. I hope this clears things up!

How does an ArrayBuffer represent an image (Javascript)?

An ArrayBuffer in JavaScript represents an image by storing the binary data of each pixel in the image. Essentially, an image is a grid of pixels, and each pixel's color is represented by bits. The size of this grid determines the resolution of the image.

When an image is saved to an ArrayBuffer, the buffer holds the binary data for every pixel. The more bit-depth (the number of bits used to represent each pixel), the more memory the ArrayBuffer requires. For instance, a higher bit-depth allows for a greater range of colors but also increases the file size.

The maximum size of an ArrayBuffer is 2GB (approximately 2,147,483,647 bytes). This is sufficient to store approximately 85 images with 4K resolution (3840 x 2160 pixels) at a 24-bit color depth.



Thank you for reading.