Arc 1 · Make it fly  /  Tutorial 01 of 16

Cooked control
& the flight loop

By the end of this page you will have a script that counts down, lifts off, tips over once, and shuts down at a target apoapsis. It will be crude — that is deliberate. Every tutorial after this one earns its keep by beating it.

pitch-over MECO altitude downrange
Tutorial 01 flight profile
Prelaunch

The vocabulary

Everything below is what you need for this tutorial and nothing more. Skim it, don't memorise it — you'll come back.

Three rules that bite everyone Every statement ends with a period. Identifiers and keywords are case-insensitive. And when your script ends, kOS hands control back to the game — so a script that exits still leaves your rocket unsteered.
Terminal & flow
CLEARSCREEN.Wipe the terminal.
PRINT x.Print a value or string on the next line.
PRINT x AT (col,row).Print at a fixed spot — use this in loops so text doesn't scroll away.
WAIT 2.5.Pause this many seconds. WAIT 0. yields one physics tick.
WAIT UNTIL cond.Block until the condition is true. Nothing else in your script runs meanwhile.
UNTIL cond { }Loop the body until the condition becomes true.
FROM {…} UNTIL {…} STEP {…} DO { }Counting loop. Verbose, but it's what kOS gives you.
Values
SET x TO 5.Assign a value once, right now.
LOCK x TO expr.Bind an expression. It re-evaluates every time something reads x.
UNLOCK x.Break the binding.
Control
SAS OFF. RCS OFF.Turn stock autopilot off. SAS fights kOS steering — always disable it.
LOCK THROTTLE TO 1.Throttle, 0 to 1.
LOCK STEERING TO HEADING(90,90).Point the ship. This is cooked control: you give a direction, kOS flies to it.
HEADING(az, pitch)az = compass bearing (90 = east). pitch = degrees above the horizon, so 90 is straight up.
STAGE.Fire the next stage. Same as pressing space.
Telemetry
SHIP:ALTITUDEMetres above sea level.
ALT:RADARMetres above the terrain directly below.
SHIP:APOAPSIS / :PERIAPSISMetres. Available even while suborbital.
SHIP:VELOCITY:SURFACE:MAGSpeed relative to the rotating surface, m/s.
SHIP:MASSCurrent total mass in tonnes.
SHIP:AVAILABLETHRUSTThrust of active, non-flamed-out engines at full throttle, in kilonewtons.
TIME:SECONDSUniversal time in seconds. Your only clock.
Units matter and they're friendly here. Mass is in tonnes, thrust in kilonewtons. kN ÷ t = m/s² exactly, so you can divide thrust by mass and read the answer as acceleration with no conversion factor at all.
Ignition

SET freezes. LOCK follows.

This is the single idea that makes kOS click. Get it now and the next fifteen tutorials are mostly arithmetic.

SET evaluates the right-hand side immediately and stores the resulting number. LOCK stores the expression itself and re-runs it every time the value is read. Steering and throttle are read many times per second, so a locked expression becomes a live control law.

set-vs-lock.ks
// Run this on the pad and watch the difference.
SET frozen TO SHIP:ALTITUDE.      // a number, captured once
LOCK live TO SHIP:ALTITUDE.        // an expression, re-read forever

PRINT frozen.   // e.g. 76
PRINT live.     // e.g. 76

WAIT 10.       // (go fly around, or just let time pass)

PRINT frozen.   // still 76 — it was never anything but a number
PRINT live.     // whatever the altitude is right now

So this is a control law that works:

works
LOCK STEERING TO HEADING(90, 90 - SHIP:ALTITUDE / 500).

…and this one points at a fixed direction forever, because the subtraction happened once, on the pad, when altitude was about 76 m:

does not work
SET STEERING TO HEADING(90, 90 - SHIP:ALTITUDE / 500).
The cost of LOCK. A locked expression runs on every read. Put something expensive inside one — a loop, a list scan — and you will feel it. That's a real constraint you'll hit in Tutorial 03.
Liftoff

Holding control

A kOS program runs top to bottom and then quits. Your rocket is only under script control for as long as the script is still executing — so the script has to stay alive for the whole ascent.

Two ways to stay alive. WAIT UNTIL blocks silently until a condition flips:

blocking
WAIT UNTIL SHIP:ALTITUDE > 1000.

An UNTIL loop stays alive and lets you do work each pass — printing telemetry, checking several conditions, changing the control law:

polling
UNTIL SHIP:APOAPSIS > 75000 {
  PRINT "ALT  " + ROUND(SHIP:ALTITUDE) + "   " AT (0,4).
  PRINT "AP   " + ROUND(SHIP:APOAPSIS) + "   " AT (0,5).
  WAIT 0.   // yield one tick — never spin a loop without this
}
Always yield A loop with no WAIT in it burns the whole instruction budget every tick and makes the game stutter. WAIT 0. gives up the rest of the tick. Put one in every loop you write.

The trailing spaces in those PRINT … AT strings aren't a typo. Printing at a fixed position overwrites characters but doesn't clear the rest of the line, so 1000 replacing 10000 leaves a stray digit behind.

A countdown

countdown
FROM {LOCAL t IS 5.} UNTIL t = 0 STEP {SET t TO t - 1.} DO {
  PRINT "T-MINUS " + t.
  WAIT 1.
}
Ascent

How hard does it push?

One number decides whether you leave the pad at all, and it isn't thrust. Work this out yourself before opening the panel.

Thrust-to-weight ratio is thrust divided by weight — and weight is mass times the local gravitational acceleration, not a constant:

TWR = Fm g(h)
g(h) = g0 R2(R + h)2

Kerbin: g0 = 9.81 m/s², R = 600 000 m

Derive these three
  1. Write the vertical acceleration of a rocket climbing straight up, in terms of F, m and g. Then rewrite it using only g and TWR. Ignore drag.
  2. Starting from rest at TWR = 1.6, how long to reach 100 m/s if TWR stayed constant? (It won't — that's question 3.)
  3. Throttle is pinned at 100% and the engine's thrust never changes. Explain in one sentence why TWR nevertheless climbs throughout the burn, and name the two separate reasons.
Saved
Show the algebra

1 — Vertical acceleration. Two forces act along the vertical: thrust up, weight down.

a = F − m gm = Fm − g

Now substitute F = TWR · m g and the mass cancels:

a = g (TWR − 1)

Read that carefully: TWR = 1 gives zero acceleration. You hover, burning fuel, going nowhere. Below 1 you don't move. This is why a launch clamp release at TWR 1.05 feels so awful.

2 — Time to 100 m/s. a = 9.81 × (1.6 − 1) = 5.886 m/s². Then t = v/a = 100 / 5.886 ≈ 17.0 s, and you'd have covered ½at² ≈ 850 m doing it.

3 — Why TWR climbs anyway. The numerator is fixed but the denominator shrinks for two independent reasons:

  • Mass falls. You are throwing propellant overboard at a constant rate, so m drops steadily and F/m rises.
  • Gravity weakens. g(h) falls off as 1/(R+h)². At 70 km, g is about 80% of its sea-level value.

Mass is by far the bigger effect during a first stage. But there's a third thing you may have written down that isn't a TWR change at all: on Kerbin, most engines get more thrust as ambient pressure drops, so F also rises. Give yourself credit for that — SHIP:AVAILABLETHRUST already accounts for it, which is exactly why we read it live rather than hardcoding a number.

Liftoff calculator
Local g—
TWR—
Vertical accel—
To 100 m/s—
Aim for 1.4 – 1.8 at liftoff. Below 1.3 you bleed delta-v hovering; much above 2.0 and you hit thick air too fast and pay it back in drag. You'll be able to prove both of those claims yourself in Tutorial 05, once you're logging data.
Your mission

launch1.ks

Write it yourself. You have every command you need above — nothing new is required.

Requirements
  1. Print a 5-second countdown.
  2. Disable SAS. Lock throttle to full and steering to straight up, due east.
  3. Stage.
  4. Hold vertical until 1 000 m.
  5. Pitch over to 80° above the horizon, still due east, and hold it.
  6. Keep printing altitude, apoapsis and surface speed at fixed screen positions throughout.
  7. Cut the throttle when apoapsis passes 75 km, print MECO, and hand steering back to the player.
Before you fly Quicksave with F5. Use a rocket that reaches ~75 km on a single stage, or be ready to press space yourself — automatic staging is Tutorial 03. Save the file as launch1.ks in Ships/Script/ and run it with RUN launch1.
Two traps in this spec

Requirement 5 says "hold it". Requirement 7 says "hand steering back". Think about what each one means for how your script is structured — and about what happens between them if you use WAIT UNTIL for step 6.

Saved
Solution

Open after you've flown yours

If your script works and looks nothing like this, that's fine — compare the reasoning, not the characters.

Show launch1.ks
launch1.ks
// launch1.ks — fixed pitch-over ascent. Crude on purpose.

CLEARSCREEN.
SET targetAp TO 75000.
SET turnAlt TO 1000.
SET azimuth TO 90.

FROM {LOCAL t IS 5.} UNTIL t = 0 STEP {SET t TO t - 1.} DO {
  PRINT "T-MINUS " + t AT (0,0).
  WAIT 1.
}

SAS OFF.
RCS OFF.
SET pitch TO 90.
LOCK THROTTLE TO 1.
LOCK STEERING TO HEADING(azimuth, pitch).
STAGE.
PRINT "LIFTOFF          " AT (0,0).

// --- vertical climb ---
UNTIL SHIP:ALTITUDE > turnAlt {
  telemetry().
  WAIT 0.
}

// --- one crude pitch-over, then hold ---
SET pitch TO 80.
PRINT "PITCH PROGRAM    " AT (0,0).

UNTIL SHIP:APOAPSIS > targetAp {
  telemetry().
  WAIT 0.
}

// --- MECO ---
LOCK THROTTLE TO 0.
PRINT "MECO             " AT (0,0).
WAIT 1.
UNLOCK STEERING.
UNLOCK THROTTLE.
SAS ON.

FUNCTION telemetry {
  PRINT "ALT   " + ROUND(SHIP:ALTITUDE) + "    " AT (0,2).
  PRINT "AP    " + ROUND(SHIP:APOAPSIS) + "    " AT (0,3).
  PRINT "SPD   " + ROUND(SHIP:VELOCITY:SURFACE:MAG,1) + "    " AT (0,4).
  PRINT "PITCH " + pitch + "     " AT (0,5).
}

The two traps

Trap one — the pitch variable. Steering is locked to HEADING(azimuth, pitch) once, before liftoff, and never re-locked. Yet changing pitch later with a plain SET steers the rocket. That's the whole lesson of this page: LOCK captured the expression, so it re-reads whatever pitch currently holds. This is the pattern every remaining tutorial builds on — one lock at the top, a value that evolves underneath it.

Trap two — the loop, not the wait. Requirement 6 says keep printing throughout. WAIT UNTIL SHIP:APOAPSIS > targetAp. would satisfy requirements 5 and 7 perfectly and leave you staring at a frozen display for four minutes. Blocking waits are for moments; loops are for phases.

And the UNLOCK at the end matters more than it looks. Without it the script ends with steering still locked, kOS relinquishes control mid-command, and the ship tumbles. SAS ON after unlocking hands you a stable vessel.

Why the function is at the bottom. kOS reads the whole file before executing, so a FUNCTION can be declared after the code that calls it. Keeping helpers out of the flight sequence keeps the sequence readable — you'll have a lot more of both by Tutorial 03.
Break it

Five experiments

Quicksave first. Each of these teaches something the working version hides.

  1. Change the steering lock to SET STEERING TO HEADING(azimuth, pitch). and fly it. Where does the rocket point, and why that direction specifically?
  2. Delete the UNLOCK STEERING. line. Watch what happens the instant the script ends.
  3. Remove WAIT 0. from the ascent loop. Watch your framerate, then check the terminal for an instruction-limit message.
  4. Move STAGE. to before the throttle lock. Explain the delay you see.
  5. Set the pitch-over to 45° instead of 80°, on the same rocket. It will probably still reach orbit-ish. Now ask yourself the question you can't yet answer: which one was cheaper, and by how many m/s?
That last question is the entire reason Tutorial 04 exists. Guessing at ascent efficiency is how most players do it forever. You're going to measure it.