Not all data is text. Images, videos, and PDFs are binary data — raw bytes that don't map to readable characters. JavaScript strings are built for text; Node needs a different type to hold binary data, and that's what a Buffer is.
You've actually already produced one, without naming it. Leave off the encoding when reading a file:
const fs = require('fs')
const data = fs.readFileSync('photo.png')
console.log(data) // <Buffer 89 50 4e 47 0d 0a 1a 0a ...>Without 'utf8' as a second argument, readFileSync returns the file's raw bytes as a Buffer instead of trying to decode them as text — appropriate here, since PNG data isn't meant to be read as characters at all.
const buf = Buffer.from('Hello')
console.log(buf) // <Buffer 48 65 6c 6c 6f>
console.log(buf.length) // 5
console.log(buf.toString()) // 'Hello'Buffer.from() converts a string into its raw byte representation; .toString() converts it back. Each byte is shown in hexadecimal when a Buffer is logged.
The 'data' event from the streams lesson delivers each chunk as a Buffer by default — that's the actual type flowing through readStream.pipe(writeStream). Passing 'utf8' as an encoding to createReadStream(), as in the streams lesson, tells Node to convert each chunk to a string before your 'data' handler ever sees it.
You won't create Buffers directly very often in typical application code — most of the time they arrive already handed to you, from a file read, a network response, or a stream's 'data' event. Knowing what they are is what matters, so a <Buffer ...> in your console output isn't a mystery when it shows up.