Universal Robots (UR) cobots are programmed two ways: the PolyScope teach pendant and the URScript text language. This guide explains both accurately — movej vs movel, waypoints and blends, gripper I/O, TCP and payload — then lets you write real URScript on a simulated UR5e arm. No robot. No install. No vendor license.

The two ways
Every UR arm — the UR3e, UR5e, UR10e, UR16e and the larger models — ships with the same software, so the programming approach is identical across the range. There are two ways in, and experienced programmers move between them constantly.
PolyScope is the touchscreen interface on the UR teach pendant. You build a program tree by adding nodes — Waypoints, Move blocks (MoveJ / MoveL), I/O actions, and If / Loop logic. Crucially, you teach poses by physically jogging the arm to a position by hand and saving it as a waypoint, rather than typing coordinates. It is the fastest way to get a working program and the way most operators start. Under the hood, PolyScope generates URScript for everything you build.
URScript is UR’s text-based language, with a Python-like syntax. You write commands directly — movej, movel, set_digital_out, set_tcp — either inside a Script node in a PolyScope program, sent over a socket from a PC, or as a complete .script file. Text gives you full control: real variables, math, loops, functions, and threads that are awkward to express in the graphical tree. It is the same language whether you are on a UR3e or a UR16e.
The takeaway: PolyScope and URScript are not rival products — they are two views of the same robot. PolyScope is generated URScript with a friendly front end. Learning URScript is what makes you fluent, because it is what the controller actually runs.
Every graphical node you add on the teach pendant compiles to a URScript line. Once you can read that mapping, the two ways stop feeling separate.
| PolyScope node | Generated URScript |
|---|---|
| MoveJ waypoint | movej(pose, a=1.4, v=1.0) |
| MoveL waypoint | movel(pose, a=1.2, v=0.25) |
| Set DO action | set_digital_out(0, True) |
| Wait DI | while not get_digital_in(2): sync() |
| Set TCP / Payload | set_tcp(...) / set_payload(...) |
| Loop / If node | while ... : / if ... : |
URScript essentials
You can program a working UR cell with a small core of URScript. Here is the essential vocabulary, used correctly.
movej — moves in joint space. Each joint interpolates from its current angle to the target angle. Fast and efficient, but the tool follows a curved path. Best for free-air moves between stations.movel — moves the tool in a straight Cartesian line at a controlled tool speed. Use it for approach, insertion, and any move where the tool’s path matters.movep — moves the tool linearly at a constant speed with circular blends, for process paths like dispensing or gluing where steady tool velocity is required.A waypoint is a taught target pose. By default the arm stops at each one. Add a blend radius and the arm rounds the corner — it never fully stops, so a chain of moves flows smoothly and the cycle time drops. Bigger blend = smoother and faster, but the path cuts the corner more, so blends are a trade-off you tune per move.
set_digital_out(n, True/False) sets a digital output — the usual way to fire a 2-finger gripper or signal a conveyor. get_digital_in(n) reads an input, for example a part-present sensor. Picking something up is just: move to the part, set the gripper output to close, lift.
set_tcp(pose) tells the robot where the working point of the tool is relative to the flange, so movel lines are straight at the tool tip, not the wrist. set_payload(mass) tells the robot how heavy the tool-plus-part is so it controls motion accurately and its safety/force monitoring stays correct. Getting both right is essential for accuracy and safe cobot operation.
# A first URScript pick-and-place on a UR5e
set_tcp(p[0, 0, 0.15, 0, 0, 0]) # tool point: 150 mm gripper
set_payload(0.8) # 0.8 kg tool + part
movej(home_q, a=1.4, v=1.0) # joint move to a safe home pose
movel(pick_approach, a=1.2, v=0.3) # straight line above the pick
movel(pick, a=0.5, v=0.1) # straight down onto the part
set_digital_out(0, True) # close the gripper
sleep(0.4)
movel(pick_approach, a=1.2, v=0.3) # lift straight up
movel(place, a=1.2, v=0.25) # straight line to place B
set_digital_out(0, False) # open the gripper
movej(home_q, a=1.4, v=1.0) # return homeThat is real URScript — the exact same commands run on a physical UR controller. In the simulator you write this, the arm solves the inverse kinematics for each pose, and you watch it run under physics.
Learn UR programming
You do not learn robot programming by reading — you learn it by writing code and watching the arm move. This is the order that works, and it is the order the lessons follow. Every step runs against a simulated UR arm and is graded against a real goal.
Move the UR in joint space and Cartesian space; understand base vs tool frames and how the tool centre point (TCP) is defined before you write any code.
Write movej and movel to send the arm between poses; see how acceleration (a) and velocity (v) change the motion, and when each move type is right.
Use set_digital_out and read inputs with get_digital_in; open and close a gripper so the arm can actually pick something up.
Combine approach, grasp, lift, traverse, place, and release into the core cobot skill — a complete pick-and-place cycle.
Chain waypoints and add a blend radius so the arm flows smoothly through points instead of stopping at each one, for faster cycle times.
Configure set_payload and set_tcp correctly and see how they change reach, accuracy, and the safe speed of the arm.
Trigger and avoid a protective stop; understand cobot force limits and safety planes so your program stays within safe limits.
Program a complete cell — palletise parts into a pattern with no collision, under a cycle-time budget, and within force limits — and earn the pass.
Cobot safety
Universal Robots are collaborative robots — designed, with a proper risk assessment, to work near people. They achieve that mainly through force and power limiting: the controller monitors joint forces, and if it detects an unexpected contact or exceeds a configured force threshold, it triggers a protective stop — the arm halts immediately. You also define safety planes, speed limits, and reduced-speed zones in the safety configuration.
For a programmer this means two habits: keep the set_payload value accurate so force monitoring works correctly, and design moves that respect the cell’s safety limits. A simulator is the right place to learn this — you can deliberately trigger a protective stop and learn to avoid it without ever damaging a real arm or risking a person. A collaborative rating is never a substitute for a real safety assessment on a deployed cell.
Why a simulator
A UR arm costs tens of thousands and one bad move can damage tooling. The simulator lets you fail safely and repeat a task endlessly — the only way to actually build skill — at zero cost and zero risk.
UR’s official URSim is the real controller software but ships as a Linux virtual machine that beginners find heavy to install. Ours opens in a browser tab on any computer, with lessons that teach from zero.
You write the same movej, movel, set_digital_out, set_tcp and set_payload you would type into a real UR — so the language and habits transfer straight onto PolyScope and a physical controller.
Keep learning