logo

Babylon.js Market

By Lawrence

6 minutes

Download Assets

TL;DRbjs download <asset-key> saves a marketplace assetA binary game resource — a 3D model, texture, audio clip, or a zipped pack of them — that your game loads at runtime. to disk. It can be a .glb model or a zipped pack. First the command asks the server for a signed token. That token expires in seconds, so the command fetches the file right away, before the token runs out. You may already know this signed-token pattern. The new part, and the base for the next lesson, is the line it draws: download moves assets, never a component's source.

Last lesson you browsed the catalog in the BBS. You came away with a short list of names, but nothing on disk yet. This lesson turns one of those names into a file, as long as the name points to an asset. Your scaffolded game has a player, a camera, and a floor. Right now each one is a grey capsule or a flat plane. To make it look like a real game, you need real art: a spaceship model, a city block, a texture pack. That art lives in the marketplace, and one command lands it on disk:

bjs download spaceship-fighter

Watch the spinnerAn animated terminal indicator that shows a long-running command is still working and reports its progress text. closely the first time and you'll notice something odd. The command doesn't reach into your account and stream the file straight down. First it asks the server for a token. That token expires in seconds. Then the command hurries to use it before it runs out. Why hand a download a token that expires so fast, instead of just handing over the file? And why is download the right tool for that .glb, the standard one-file 3D-model format, but the wrong tool for getting a component's source code? Both answers come straight out of what the command does.

The whole trick lives in how the command talks to the server. It never streams the file on the strength of your login alone. It makes two separate requests, one after the other: the first asks may I have this? and gets back a signed, expiring link; the second uses that link to pull the bytes down. When the prose below says ask the server or the two requests, that back-to-back pair is what it means. Keep that shape in mind — it's what makes the rest fall into place.

The asset key and the login check

Start with the outside of the command: the one argument it takes, and the check it runs before it does anything else.

Two things matter here. The argument is an <asset-key>, like spaceship-fighter or city-buildings — the same key you saw on a component-or-asset row in the catalog browser. It is not a file path, and not the name of a component's source code. And before the command fetches anything, it confirms you're logged in. If you're logged out, the first thing it does is send you to bjs login. The session you saved in Lesson 1 is what unlocks every download.

The check looks at your session, not your project, so download is account-boundTied to your logged-in identity rather than to any one project, so the command works from any folder once you're authenticated.. It doesn't care which folder you're in. You can run it from anywhere. The only thing that sets where the file lands is -o, which you'll meet at the end.

Two requests: a token, then the file

Here's the part the spinner shows you. Getting the file takes two requests, not one.

The first request asks the server for permission, signed with your session. The server doesn't send back the file. It sends back three things: the file's name, a download URL, and a countdown — how many seconds that URL will keep working. It's signed, and it works for only those few seconds. The second request runs right after and fetches the file from that URL.

Splitting the work this way gives the marketplace three things at once. First, the signed URLA file URL carrying a server signature that proves the request is authorized and can't be tampered with or replayed. proves the request was allowed, so it doesn't have to re-send your credentials over the wire. The file transfer needs no auth header. Second, the protection is the expiryThe moment a token stops being valid; the download token expires seconds after it is issued, forcing an immediate fetch., not single use. The URL carries an HMAC signature that the server re-checks, and it stops working seconds after it's issued. A leaked link is worthless almost right away, because there's no time left to share it. Third, the permission request is rate-limited. Ask too fast and the first request comes back 429. The command tells you you've been rate limited and to wait, and it never reaches the file. So a short-lived signed key is what lets the file itself sit behind an open, fast, signature-checked URL.

A two-step download handshake on a dark CRT screen: first the CLI requests a short-lived signed token from the server, then races to fetch the file before the countdown expires

The countdown is why you can't "save the link and download later." By the time you paste it, it has expired. That's why the command asks for the token and fetches the file back to back.

Where the file is saved, and tracking progress

Once the second request has the file streaming, the command picks where to put it, makes the folder if needed, and writes the bytes down while it reports progress.

The output directoryThe folder a downloaded file is written to — the current directory by default, or whatever -o names. is whatever you passed to -o. If you didn't pass one, it uses the folder you ran the command in. If that folder doesn't exist yet, the command builds the whole path for you — every missing parent included — so you don't need to mkdir first. The saved file keeps the name the server chose, so you don't have to guess the extension.

The progress number is real, not just for show. The server sends a content-length header, which is the total byte count. The command adds up each chunk as it arrives and turns that ratio into the percentage the spinner shows. When the size is known, you see Downloading spaceship-fighter.glb: 42% climbing to 100. When it isn't, the spinner just spins. Either way, it ends on one green line that tells you the file and exactly where it went.

In practice, that's two choices about the flag. With no flag, the file drops in the folder you're in. With -o, it goes where your engine will look for it:

# lands spaceship-fighter.glb in the current folder
bjs download spaceship-fighter

# creates public/models if needed, lands the pack inside it
bjs download city-buildings -o public/models

Run the second one and the spinner walks through the whole flow:

⠋ Requesting download token...
⠹ Downloading city-buildings.glb (token expires in 30 seconds)...
⠸ Downloading city-buildings.glb: 42%
✔ Downloaded city-buildings.glb to public/models/city-buildings.glb

It's worth checking a 15 MB pack before you pull it. The catalog row shows the file type and size next to the key, so you know if you're fetching a 200 KB prop or a whole city. If the very first line fails with a login message, your session ran out since Lesson 1. Run bjs login to restore it, and the download works from anywhere you try it.

Not every pull is free or open. A paid asset costs credits the first time you get it, though re-downloads are free after that. An asset tied to a course needs you to own that course. So the token request can come back not only as a 429, but as "insufficient credits" or "access denied." That's a wall to clear on the website, not a bug in the command.

Why download is for assets, not source code

Notice what never entered the picture: a component, a .ts file, a line of code. Every request the command makes goes to the asset side of the marketplace, and every result is a single file written whole to disk. download moves assets, the binary game resources your engine loads at runtime. It is not how a component's source gets into your project.

That difference matters, because it's tempting to want one tool for everything:

You wantThe toolWhy
A .glb model, texture, or zipped asset packbjs download <asset-key>Assets are files; download streams the file
A built-in component's editable sourcearcade eject (next lesson)Eject copies source out of the package you already have
A catalog component you don't own yetacquire it in the catalog browser (the BBS), spending credits (next lesson)A component is source plus dependencies, not a single file

Assets are files, so a file-download command fits them well. A component is source code with its own dependencies. It lands in src/components/, not as one loose file. That's a different shape, served a different way. Reach for download and you'd get nothing back, because the asset endpoint has no component to hand you. The next lesson is the other half: how source lands in src/components/ in one steady shape, whether it's your own, the package's, or the catalog's.

Next: Get Component Source: Eject and Acquire — asking for one component and watching its whole dependency closure arrive in your folder.

Was this page helpful?

We read every note — tell us what's working and what isn't.

↑↓ NavigateEnter SelectEsc CloseCtrl+K Open Search