Learn to code — from zero
Never written a line of code? Perfect. On this page you'll make a real light blink and a real buzzer play a tune, just by writing a few simple lines — no wiring, no setup, nothing to install. If you can write a shopping list, you can do this.
What is a program?
A program is just a list of instructions a machine follows, one at a time, from top to bottom — like a recipe, or the steps of a dance. The machine is fast and very obedient: it does exactly what you wrote, in the order you wrote it. Nothing more, nothing less.
So "coding" is mostly about breaking what you imagine into small, clear steps. "Turn the red light on. Wait one second. Turn it off." That's already a program.
BASIC, and our Chip BASIC
BASIC is a programming language invented in 1964 to teach complete beginners — the name literally stands for "Beginner's All-purpose Symbolic Instruction Code". It reads almost like plain English, which is exactly why we use it here.
Our version is called Chip BASIC, and it's two things in one that you use together in the same program: the timeless BASIC core — line numbers, GOTO, IF…THEN, LET, variables and maths, the same ideas as BASIC since 1964 — plus our own commands that talk to the board: LED, WAIT, SOUND, PLAY, BUTTON and whole MISSIONs.
10 LET a = 0 20 LED green, on 30 WAIT 1s 40 LED green, off 50 a = a + 1 60 IF a < 3 THEN GOTO 20 70 MISSION "ALLARME"
One program, both layers at once: classic BASIC (LET, maths, IF…THEN, GOTO) runs the loop, and our commands (LED, WAIT, MISSION) make the board act. There's no mode to switch — you just mix them on whatever line you like.
Chip BASIC's golden rule: every line starts with a number (10, 20, 30…). Those numbers set the order the lines run in and give each line a name you can jump to. Count by tens, not 1-2-3, so you can slip new lines in between later — a line between 10 and 20 becomes 15.
How Text to Chip works
You write BASIC. The browser translates it into a list of tiny, simple instructions (we call it bytecode). A small "player" then runs those instructions — either in the Simulator on screen, or on a real chip (an ESP32-S3) connected by USB.
The clever part: it's the same translated program in both places. What you see blink in the simulator is what blinks on the real board. So you can build and test everything with no hardware, then run the exact same thing on a real chip later.
Your first program
Let's blink the red light. Open the IDE, make sure the Simulator is selected, and type these five lines into the editor:
10 LED red, on 20 WAIT 1s 30 LED red, off 40 WAIT 1s 50 GOTO 10
Now press ▶ run (top-left). The red light turns on for a second, off for a second, on again… forever. You just wrote a program! Press ■ stop to end it.
Here's what each line says:
10 LED red, on— turn the red light on.20 WAIT 1s— wait 1 second (the light stays on while we wait).30 LED red, off— turn the red light off.40 WAIT 1s— wait another second (now it stays off).50 GOTO 10— jump back to line 10 and do it all again. That's what makes it blink forever.
1s to 0.2s on both WAIT lines and run again — the light blinks much faster. Changing numbers and watching what happens is how you learn.How the lines run
The player starts at the smallest line number and walks down: 10, then 20, then 30… If it reaches the end with nothing left, it stops.
GOTO changes that path: it jumps to whatever line you name. GOTO 10 sends the player back to line 10, which creates a loop — a piece that repeats. Loops are how a traffic light keeps cycling and a blink keeps blinking.
The toolbox — every instruction
These are all the "words" Chip BASIC understands and turns into real action — the classic BASIC ones and our board commands side by side. Each card shows the shape of the instruction, what it does, and a tiny example. You don't need to memorise them — peek back here whenever you need one.
Lights, time & repeating
LED <colour>, <on|off>Turns one of the three coloured lights on or off. Colours are red, yellow and green.
10 LED green, on
WAIT <n>s · WAIT <n>msPauses the program. Use s for seconds or ms for milliseconds (1000 ms = 1 s). Decimals like 0.5s are fine. Whatever was on or off stays that way while it waits.
20 WAIT 2s
GOTO <line>Jumps to another line number — the program carries on from there. Use it to loop, or to skip ahead.
50 GOTO 10
ENDStops the program cleanly. (A program that loops with GOTO never reaches an END — you stop it with the ■ button.)
90 END
REM <note>A note to yourself. The machine ignores everything after REM, so use it to explain what a part does.
10 REM blink the light
<statement> : <statement>Puts two instructions on the same line, separated by : — handy for short steps that belong together.
10 LED red, on : WAIT 1s
Making decisions
IF <condition> THEN <action>Makes a decision: if the condition is true, then it does the action; otherwise it skips it. Conditions compare numbers with >, <, = and can join with AND.
30 IF a > 5 THEN LED red, on
BUTTON("A")Is true while button A is being pressed. Put it inside an IF to react to a press.
30 IF BUTTON("A") THEN SOUND "coin"Remembering numbers
LET <var> = <value> ( a … z )Remembers a number under a name. You have 26 little boxes named a to z. LET is optional — a = 0 works too.
10 LET a = 0
+ - * / > < = AND ABS()Do arithmetic with + - * /, compare with > < =, combine conditions with AND, and get a positive value with ABS(). Great for counting things.
20 a = a + 1
Making sound
SOUND "<name>"Plays a sound on the buzzer: a short beep, or a named tune — mario, tada, coin, win, siren, scale.
40 SOUND "coin"
PLAY "<notes>"Plays your own melody. Write notes as a letter + octave: c5, e5, g5. A - is a rest (silence), and :500 after a note sets its length in milliseconds.
50 PLAY "c5 e5 g5 c6"
Reusable blocks & whole missions
GOSUB <line> … RETURNJumps to a line like GOTO, but RETURN sends it back to where it came from. Use it for a block you want to reuse from several places (a "mini-program").
10 GOSUB 100
MISSION "<NAME>" [WITH k=v …]Runs a whole ready-made behaviour in one line — a traffic light, a metronome, an alarm… Tune it with WITH name=value. More on this just below.
10 MISSION "SEMAFORO" WITH green=4s
Move, listen & see (robot + AI)
MOVE <left> <right>Drives the two wheels: the first number is the left wheel, the second the right. Each speed is -255…255 (minus = backwards, 0 = stop; ~160 is a good cruise — small motors stall below ~80). Different speeds steer: MOVE 40 160 curves left. It's sticky — the robot keeps going until the next MOVE, so stop with MOVE 0 0.
10 MOVE 160 160
SERVO <angle>Turns a servo motor to an angle, from 0 to 180 degrees.
20 SERVO 90
VOICE()The last word the robot heard with its microphone (the AI). Use it inside an IF. Words it knows: go, left, right, stop.
10 IF VOICE()="go" THEN MOVE 160 160
SEE()What the robot's camera recognizes (the AI). Use it inside an IF. Things it knows: person, ball, hand.
10 IF SEE()="person" THEN SOUND "coin"
SENSOR("<name>") · AREAD()Reads an analog sensor — a number from 0 to 4095, like a distance or a light level. Use it inside an IF to react when it's small or large.
10 IF SENSOR("radar") < 20 THEN MISSION "ALLARME"Ready-made missions
A mission is a complete behaviour someone already built for you. Instead of writing the whole traffic-light dance line by line, you call it in one line:
10 MISSION "SEMAFORO" WITH green=4s yellow=1s red=4s beep=slow 20 GOTO 10
The WITH part tweaks it: how long green lasts, the beep speed, and so on. Each mission allows certain settings within safe limits (a traffic light won't let you switch red off — that would be unsafe). The available ones are in the Library in the sidebar: SEMAFORO (traffic light), PIANOLA (keyboard), METRONOMO (beat) and ALLARME (alarm).
Easiest way to change a mission's settings: click the MISSION line in the editor — a little form opens with every option, ready to adjust.
The mission catalog
Here's every mission you can run in the simulator today, by tier. Open any one from the Library (the grid in the sidebar) — or just write its line, e.g. MISSION "RADAR". Each one can be tuned with WITH, just like the traffic light above.
Junior
MISSION "SEMAFORO"— Traffic light · RGB cycleMISSION "METRONOMO"— Metronome · BPM beatMISSION "ALLARME"— Alarm · Beep triggerMISSION "TIMER"— Timer · CountdownMISSION "CONTAPASSI"— Step counter · IMU
Maker
MISSION "MOVIMENTO"— Motion · PIR sensorMISSION "TERMOMETRO"— Thermometer · Temp thresholdMISSION "NOTTURNA"— Night light · Light sensorMISSION "BARRIERA"— Barrier · Servo gateMISSION "POMPA"— Pump / timer · scheduled relayMISSION "HIGHWAY"— Highway Road · Avoid obstaclesMISSION "LINEFOLLOW"— Line Follower · Follow a line
Pro · AI
MISSION "ROBOCAR"— Voice car · autonomous · 3 wheelsMISSION "VOICE"— Voice · edge-AI · commandsMISSION "TROVAPALLA"— Find the ball · Camera hunt
It's parameterizable. Add a WITH clause to adjust the timings, the beep and the button:
MISSION "SEMAFORO" WITH green=6s yellow=1s red=4s beep=off|slow|fast button=on|off minGreen=2s
Ranges: green 2–15s, yellow 0.5–3s, red 2–15s, minGreen 2–10s. beep is off, slow or fast; button is on or off. Durations accept decimals or milliseconds (0.5s, 1500ms). Set just the parameters you care about, e.g. MISSION "SEMAFORO" WITH green=4s beep=fast.
The pedestrian button
When button=on, pressing the button is a crossing request: the green LED blinks immediately to acknowledge it, then the light turns to red as soon as green has lasted at least minGreen (so a press can't stop traffic instantly). On the simulator, click the on-screen button; on hardware, wire it to GPIO 6 (active-low, to GND).
Fixed safety guardrails
Some things can't change — they're safety guardrails: the lights always cycle green → yellow → red, one light at a time, the walk beep sounds only on red, and a button press can never cut green below minGreen.
Let the assistant help (the @ helper)
Stuck, or in a hurry? At the bottom of the editor there's an @ describe a change box. Type what you want in plain words — "make the light blink faster" or "play a happy tune when I press the button" — and the assistant writes or edits the BASIC for you.
It always answers in real BASIC lines, right in the editor — so you can read them, run them, and learn from them. The assistant is a teacher that shows its work, not a black box.
Auto-run
There's an Auto-run toggle in the topbar, on by default. When the program changes and compiles cleanly, it's loaded and run automatically on the active backend — so you see the result live without clicking Upload & Run yourself.
A few complete programs
Paste any of these into the editor and press run. Then change a number and see what happens — that's the whole game.
A traffic light
10 LED red, on 20 WAIT 5s 30 LED red, off 40 LED yellow, on 50 WAIT 1s 60 LED yellow, off 70 LED green, on 80 WAIT 5s 90 LED green, off 100 GOTO 10
Red for 5 seconds, yellow for 1, green for 5, then GOTO 10 starts the cycle over. Try changing the wait times.
Press the button → coin sound
10 IF BUTTON("A") THEN SOUND "coin"
20 GOTO 10Line 10 checks the button over and over (line 20 loops straight back to it). Hold button A in the simulator and you'll hear the coin tune.
Play a little melody
10 PLAY "c5 e5 g5 c6" 20 END
Four notes going up, then it ends. Change the notes — try "c5 d5 e5 f5 g5" — and run it again.
Going further
Those building blocks look small, but they combine into anything — a handful of commands plus a little logic is all a real program is. Here are the patterns that take you from "blink a light" to something that thinks.
Repeat an exact number of times
10 LET a = 0 20 LED red, on 30 WAIT 0.3s 40 LED red, off 50 WAIT 0.3s 60 a = a + 1 70 IF a < 5 THEN GOTO 20 80 SOUND "tada" 90 END
A variable is a little box that remembers a number. Here a counts the blinks: line 60 adds one each loop, and line 70 keeps jumping back until a reaches 5 — then a fanfare. Change the 5 to repeat as often as you like.
Reuse a block — GOSUB and RETURN
10 GOSUB 100 20 WAIT 1s 30 GOSUB 100 40 END 100 SOUND "coin" 110 LED green, on 120 WAIT 0.5s 130 LED green, off 140 RETURN
Write a block once, use it everywhere. GOSUB 100 jumps to line 100; RETURN sends control back to right after the call. Lines 100–140 are a "beep + flash" block the main program runs twice. Big programs are built from small reusable blocks exactly like this.
Remember a state
10 LET a = 0 20 IF a = 0 THEN LED red, on 30 IF a = 1 THEN LED green, on 40 WAIT 0.5s 50 LED red, off 60 LED green, off 70 a = 1 - a 80 GOTO 20
A variable can hold which step you're on, not just a count. Here a is whose turn it is — red (0) or green (1) — and a = 1 - a flips it between 0 and 1 every loop, so the lights alternate. That tiny memory is how a program decides over time.
Maths and combined conditions
10 LET a = 10 20 LET b = 3 30 IF a > 5 AND b < 5 THEN SOUND "coin" 40 LET a = a - b 50 IF a = 7 THEN LED green, on 60 END
You can do arithmetic anywhere a number goes, and join tests with AND. Line 30 fires only if both conditions are true; line 40 updates a with a subtraction. With + - * /, > < =, AND and ABS() you can express real rules.
A robot is a sequence of missions
10 MISSION "SEMAFORO" WITH green=2s yellow=1s red=2s 20 MISSION "METRONOMO" WITH beat=0.4s bar=4 30 GOTO 10
The biggest idea: a whole project is just missions in a row, steered by the BASIC you've learned. Each line here is a complete behaviour; GOTO 10 loops the show. Swap the missions, add an IF to choose between them, count rounds with a variable — and you're programming a robot.
Where your program runs
https://textochip.com provides. Firefox/Safari and mobile can still use the Simulator.Runs entirely in the browser. Works for everyone, any OS. Best place to start.
The reference board. Flash the Zephyr firmware once, then upload & run over USB.
The firmware is Zephyr, so the same code targets new chips — only a tiny HAL file changes. Nordic nRF54L next.
The workflow (same for every backend)
- Type @ and your request on a line in the editor (the AI writes the BASIC), or pick a ready program from the Library in the sidebar. The program is numbered BASIC you edit directly.
- Choose where it runs with the Simulator / Real board toggle.
- Compile — the console shows the result (instruction count and any warnings).
- Upload & Run — sends the program and starts the VM (with Auto-run on, this happens after every change).
- Stop halts it; type an
OVERRIDE:command on a line (e.g.OVERRIDE: STOPorOVERRIDE: SET 4 0) and press Enter to inject one instruction live, without re-uploading.
Make it run on its own (Save to board)
On a real board, press Save to board to store the program in the board's flash and run it on every boot. Now you can unplug the PC: power the board from any USB charger or battery and it runs on its own — press RST to restart it. The program stays until you save a new one or press Autorun off. This is the whole point: you build the behaviour in the browser, then the chip keeps doing it with no computer attached.
1 · Simulator (no hardware)
- Open
https://textochip.comin any browser. - Leave the toggle on Simulator (it auto-connects).
- Click Upload & Run. The on-screen LEDs, buzzer indicator and button mirror exactly what a real board would do.
- For programs that read the button, click the on-screen button to drive the input pin.
The simulator is a TypeScript VM with the same instruction set and tick model as the firmware, so a program that works here behaves identically on the board.
2 · ESP32-S3 (recommended, verified)
Reference board: Freenove ESP32-S3-WROOM (USB-C, two USB ports). You flash the generic Zephyr VM firmware once; after that you upload bytecode from the IDE as many times as you like — no re-flashing to change the program.
Get the firmware
The board runs a small generic VM firmware built with Zephyr. It lives in its own open repo, textochip-runtime; you build and flash it once from there, then upload programs from the browser. The full, copy-pasteable steps live in that repo's README.
What you need
- ESP32-S3 board + USB-C cable (the Freenove exposes two USB ports — both are used)
- Breadboard, 3 LEDs (red/yellow/green) + ~220–330 Ω resistors
- A passive piezo buzzer and a push button (optional)
- The Zephyr toolchain: a
westworkspace + the Zephyr SDK (see the Zephyr Getting Started guide)
Wiring
| Component | Pin | Wiring |
|---|---|---|
| Green LED | GPIO 1 | → resistor → GND |
| Yellow LED | GPIO 2 | → resistor → GND |
| Red LED | GPIO 4 | → resistor → GND |
| Buzzer (passive) | GPIO 5 | → GND |
| Button A | GPIO 6 | → GND (internal pull-up; active-low) |
These pins come from the board profile (lib/boardProfile.ts); the compiler bakes them into the bytecode. GPIO 1/2/4/5/6 are safe digital I/O on the ESP32-S3.
Build & flash the firmware (one time)
- Set up the Zephyr toolchain — a
westworkspace and the Zephyr SDK (Zephyr's Getting Started guide). The firmware sources are in their own repo,textochip-runtime(clone it). - From the
textochip-runtimeclone, build for the ESP32-S3:west build -b esp32s3_devkitc/esp32s3/procpu zephyr
Runwest packages pip --installonce soesptool ≥ 5.0.2is on yourPATHat flash time. - Plug in the board's “USB UART” port (the CH343 chip) and flash with
west flash— esptool's auto-reset into download mode works on this port. - If a flash can't auto-reset (e.g. the firmware is already running): hold BOOT, tap RST, release BOOT, then
west flashagain. - Two ports, two jobs: you flash over the “USB UART” port, but you connect the IDE over the “USB OTG” (native-USB) port — do not swap them.
Connect & run from the IDE
- Open
https://textochip.comin Chrome/Edge/Brave. - Switch the toggle to Real board.
- Click Connect board and pick the board's “USB OTG” (native-USB) port — it shows as
CDC_ACM_serial_backend. (Not the “USB UART” port you flashed from.) - Click Upload & Run. Use Stop and Override at runtime.
west flash; opening it in the browser would reset the chip (you'd see WARN: no PONG).3 · Other boards (Zephyr)
Because the firmware is Zephyr, the very same VM, ISA and missions run on any board Zephyr supports — only a tiny HAL file changes. The browser compiler, the serial protocol and your .bas programs stay identical, so the whole IDE flow is unchanged from board to board.
What changes per board
Two things only: the GPIO numbers in lib/boardProfile.ts (the compiler bakes them into the bytecode), and one HAL implementation — about 9 functions mapping pins/PWM/clock to that chip. Then build for the target with west build -b <board> zephyr.
Nordic nRF54L (incoming)
The next target is the nRF54LM20 DK. Build it with the nRF Connect SDK (Nordic's Zephyr): west build -b nrf54lm20dk/nrf54lm20a/cpuapp zephyr, then west flash. Same IDE flow over Web Serial; BLE OVERRIDE and edge-AI missions on the Axon NPU come next.
The language, briefly
A first program — a looping traffic light (Project 01):
10 LED red, on 20 WAIT 5s 30 LED red, off 40 LED yellow, on 50 WAIT 1s 60 LED yellow, off 70 LED green, on 80 WAIT 5s 90 LED green, off 100 GOTO 10
The executable subset compiles to bytecode: LED, WAIT, GOTO, GOSUB/RETURN, IF … THEN, LET, arithmetic and comparisons (>= <= <> included), SOUND/PLAY (beeps & melodies), BUTTON("A"), the robot + edge-AI commands MOVE, SERVO, VOICE(), SEE() (objects and colours like "yellow") with SEEX() (where it is in the frame, 0-100) and SEESIZE() (how big it looks, 0-100: bigger means closer), DISTANCE() (centimetres) and SENSOR()/AREAD(), and MISSION blocks (e.g. a complete pedestrian crossing). Statements that need hardware this board doesn't have (GPS, a display…) still compile — they emit a no-op and a console warning instead of failing.
Tips & common mistakes
- Every line needs a number. A line without one (just
LED red, on) won't run — write10 LED red, on. - Count by tens (10, 20, 30) so you can slip a new line in between later.
- Times need a unit:
WAIT 2s(seconds) orWAIT 500ms(milliseconds). A bareWAIT 2means 2 milliseconds — almost nothing! - A GOTO loop never stops on its own. That's usually what you want (a blinking light) — press the ■ stop button to end it.
- The console talks to you. If something's wrong, the console under the editor shows a yellow warning or a red error with the line number. Read it — it's trying to help.
- Small steps win. Run often. Change one thing, run, look. You learn far more from ten tiny experiments than one big guess.
Troubleshooting
- No “Connect board” button / it does nothing: you're not on a Chromium desktop browser, or not on HTTPS. Use Chrome/Edge/Brave on
https://textochip.com. - “no PONG” on connect: you probably opened the “USB UART” port (which resets the chip). Pick the “USB OTG” port (
CDC_ACM_serial_backend) instead; if it still fails, press RST, wait ~3 s, reconnect. - Port missing on Linux: add yourself to
dialout(sudo usermod -a -G dialout $USER) and log out/in. - Nothing lights up: check the LED polarity and that the wired pins match the board profile.