diff --git a/build.gradle b/build.gradle
index f56043393ff3c268892159169e5b94e4ffc67569..8949c46329eecca3e747f235501620cce4b2527e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -41,6 +41,7 @@ dependencies {
 	compile(group: "de.hhu.stups", name: "de.prob2.kernel", version: "3.2.10-SNAPSHOT", changing: true)
 	compile(group: "io.github.spencerpark", name: "jupyter-jvm-basekernel", version: "2.0.0-SNAPSHOT", changing: true)
 	compile(group: "org.jetbrains", name: "annotations", version: "16.0.1")
+	compile(group: "se.sawano.java", name: "alphanumeric-comparator", version: "1.4.1")
 }
 
 sourceCompatibility = "1.8"
diff --git a/notebooks/tests/animate.ipynb b/notebooks/tests/animate.ipynb
index af0e4be8c8c65138ffc3cf59105422fec6d5919d..24c67c813ac16e5b4c57816bab8be085f83c77dd 100644
--- a/notebooks/tests/animate.ipynb
+++ b/notebooks/tests/animate.ipynb
@@ -31,8 +31,9 @@
      "data": {
       "text/plain": [
        ":exec OPERATION [PREDICATE]\n",
+       ":exec OPERATION_ID\n",
        "\n",
-       "Execute an operation with the specified predicate"
+       "Execute an operation with the specified predicate, or by its ID"
       ]
      },
      "execution_count": 2,
@@ -101,7 +102,8 @@
        "Sets: (none)\n",
        "Constants: (none)\n",
        "Variables: (none)\n",
-       "Operations: (none)\n"
+       "Operations: \n",
+       "0: $initialise_machine()"
       ]
      },
      "execution_count": 5,
@@ -122,12 +124,12 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "[2018-05-23 12:33:10,169, T+6055] \"Shell-0\" de.prob.cli.PrologProcessProvider.makeProcess(PrologProcessProvider.java:64): [INFO] Starting ProB's Prolog Core. Path is /Users/david/.prob/prob2-3.2.10-SNAPSHOT/probcli.sh\n",
-      "[2018-05-23 12:33:11,574, T+7460] \"Shell-0\" de.prob.cli.PortPattern.setValue(PortPattern.java:30): [INFO] Server has started and listens on port 52196\n",
-      "[2018-05-23 12:33:11,575, T+7461] \"Shell-0\" de.prob.cli.InterruptRefPattern.setValue(InterruptRefPattern.java:29): [INFO] Server can receive user interrupts via reference 1828\n",
-      "[2018-05-23 12:33:11,580, T+7466] \"ProB Output Logger for instance 5b68ad44\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] -- starting command loop --\u001b[0m\n",
-      "[2018-05-23 12:33:11,598, T+7484] \"ProB Output Logger for instance 5b68ad44\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] Connected: 127.0.0.1\u001b[0m\n",
-      "[2018-05-23 12:33:11,866, T+7752] \"ProB Output Logger for instance 5b68ad44\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] loading_classical_b(parser_version(2018-05-16 13:34:59.07),Counter,[/Users/Shared/Uni/SHK/ProB2/prob2-jupyter-kernel/notebooks/tests])\u001b[0m\n"
+      "[2018-05-23 15:13:10,940, T+6251] \"Shell-0\" de.prob.cli.PrologProcessProvider.makeProcess(PrologProcessProvider.java:64): [INFO] Starting ProB's Prolog Core. Path is /Users/david/.prob/prob2-3.2.10-SNAPSHOT/probcli.sh\n",
+      "[2018-05-23 15:13:12,528, T+7839] \"Shell-0\" de.prob.cli.PortPattern.setValue(PortPattern.java:30): [INFO] Server has started and listens on port 56654\n",
+      "[2018-05-23 15:13:12,530, T+7841] \"Shell-0\" de.prob.cli.InterruptRefPattern.setValue(InterruptRefPattern.java:29): [INFO] Server can receive user interrupts via reference 3008\n",
+      "[2018-05-23 15:13:12,533, T+7844] \"ProB Output Logger for instance 7bd5a630\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] -- starting command loop --\u001b[0m\n",
+      "[2018-05-23 15:13:12,554, T+7865] \"ProB Output Logger for instance 7bd5a630\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] Connected: 127.0.0.1\u001b[0m\n",
+      "[2018-05-23 15:13:12,842, T+8153] \"ProB Output Logger for instance 7bd5a630\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] loading_classical_b(parser_version(2018-05-16 13:34:59.07),Counter,[/Users/Shared/Uni/SHK/ProB2/prob2-jupyter-kernel/notebooks/tests])\u001b[0m\n"
      ]
     },
     {
@@ -163,6 +165,16 @@
    "execution_count": 7,
    "metadata": {},
    "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[2018-05-23 15:13:13,175, T+8486] \"ProB Output Logger for instance 7bd5a630\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] % Runtime for SOLUTION for SETUP_CONSTANTS: 120 ms (walltime: 150 ms)\u001b[0m\n",
+      "[2018-05-23 15:13:13,176, T+8487] \"ProB Output Logger for instance 7bd5a630\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] % Runtime for SOLUTION for SETUP_CONSTANTS: 0 ms (walltime: 0 ms)\u001b[0m\n",
+      "[2018-05-23 15:13:13,182, T+8493] \"ProB Output Logger for instance 7bd5a630\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] % Runtime for SOLUTION for SETUP_CONSTANTS: 0 ms (walltime: 0 ms)\u001b[0m\n",
+      "[2018-05-23 15:13:13,183, T+8494] \"ProB Output Logger for instance 7bd5a630\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] % Runtime for SOLUTION for SETUP_CONSTANTS: 0 ms (walltime: 0 ms)\u001b[0m\n"
+     ]
+    },
     {
      "data": {
       "text/plain": [
@@ -170,7 +182,12 @@
        "Sets: (none)\n",
        "Constants: min_value, max_value\n",
        "Variables: value\n",
-       "Operations: add(diff)\n"
+       "Operations: \n",
+       "0: $setup_constants()\n",
+       "1: $setup_constants()\n",
+       "2: $setup_constants()\n",
+       "3: $setup_constants()\n",
+       "More operations may be available (MAX_OPERATIONS/MAX_INITIALISATIONS reached)"
       ]
      },
      "execution_count": 7,
@@ -227,7 +244,7 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "[2018-05-23 12:33:12,406, T+8292] \"ProB Output Logger for instance 5b68ad44\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] % Runtime for SOLUTION for SETUP_CONSTANTS: 80 ms (walltime: 80 ms)\u001b[0m\n"
+      "[2018-05-23 15:13:13,491, T+8802] \"ProB Output Logger for instance 7bd5a630\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] % Runtime for SOLUTION for SETUP_CONSTANTS: 20 ms (walltime: 20 ms)\u001b[0m\n"
      ]
     },
     {
@@ -257,7 +274,12 @@
        "Sets: (none)\n",
        "Constants: min_value, max_value\n",
        "Variables: value\n",
-       "Operations: add(diff)\n"
+       "Operations: \n",
+       "4: $initialise_machine()\n",
+       "5: $initialise_machine()\n",
+       "6: $initialise_machine()\n",
+       "7: $initialise_machine()\n",
+       "More operations may be available (MAX_OPERATIONS/MAX_INITIALISATIONS reached)"
       ]
      },
      "execution_count": 11,
@@ -316,9 +338,9 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "[2018-05-23 12:33:12,769, T+8655] \"ProB Output Logger for instance 5b68ad44\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] \u001b[0m\n",
-      "[2018-05-23 12:33:12,769, T+8655] \"ProB Output Logger for instance 5b68ad44\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] ALL OPERATIONS COVERED\u001b[0m\n",
-      "[2018-05-23 12:33:12,770, T+8656] \"ProB Output Logger for instance 5b68ad44\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] \u001b[0m\n"
+      "[2018-05-23 15:13:13,866, T+9177] \"ProB Output Logger for instance 7bd5a630\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] \u001b[0m\n",
+      "[2018-05-23 15:13:13,867, T+9178] \"ProB Output Logger for instance 7bd5a630\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] ALL OPERATIONS COVERED\u001b[0m\n",
+      "[2018-05-23 15:13:13,868, T+9179] \"ProB Output Logger for instance 7bd5a630\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] \u001b[0m\n"
      ]
     },
     {
@@ -344,7 +366,15 @@
     {
      "data": {
       "text/plain": [
-       "2"
+       "Machine: Counter\n",
+       "Sets: (none)\n",
+       "Constants: min_value, max_value\n",
+       "Variables: value\n",
+       "Operations: \n",
+       "8: add(-3)\n",
+       "9: add(-2)\n",
+       "10: add(-1)\n",
+       "11: add(0)"
       ]
      },
      "execution_count": 15,
@@ -353,7 +383,7 @@
     }
    ],
    "source": [
-    "value"
+    ":browse"
    ]
   },
   {
@@ -364,7 +394,7 @@
     {
      "data": {
       "text/plain": [
-       "Executed operation add"
+       "2"
       ]
      },
      "execution_count": 16,
@@ -373,7 +403,7 @@
     }
    ],
    "source": [
-    ":exec add diff=-1"
+    "value"
    ]
   },
   {
@@ -384,7 +414,7 @@
     {
      "data": {
       "text/plain": [
-       "1"
+       "Executed operation add"
       ]
      },
      "execution_count": 17,
@@ -392,9 +422,123 @@
      "output_type": "execute_result"
     }
    ],
+   "source": [
+    ":exec add diff=-1"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "Machine: Counter\n",
+       "Sets: (none)\n",
+       "Constants: min_value, max_value\n",
+       "Variables: value\n",
+       "Operations: \n",
+       "12: add(-2)\n",
+       "13: add(-1)\n",
+       "14: add(0)\n",
+       "15: add(1)"
+      ]
+     },
+     "execution_count": 18,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    ":browse"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "1"
+      ]
+     },
+     "execution_count": 19,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
     "value"
    ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 20,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "Executed operation 15"
+      ]
+     },
+     "execution_count": 20,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    ":exec 15"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "Machine: Counter\n",
+       "Sets: (none)\n",
+       "Constants: min_value, max_value\n",
+       "Variables: value\n",
+       "Operations: \n",
+       "8: add(-3)\n",
+       "9: add(-2)\n",
+       "10: add(-1)\n",
+       "11: add(0)"
+      ]
+     },
+     "execution_count": 21,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    ":browse"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "metadata": {},
+   "outputs": [
+    {
+     "ename": "CommandExecutionException",
+     "evalue": ":exec: Cannot specify a predicate when executing an operation by ID",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[1m\u001b[31m:exec: Cannot specify a predicate when executing an operation by ID\u001b[0m"
+     ]
+    }
+   ],
+   "source": [
+    ":exec 10 1=1"
+   ]
   }
  ],
  "metadata": {
diff --git a/src/main/java/de/prob2/jupyter/commands/BrowseCommand.java b/src/main/java/de/prob2/jupyter/commands/BrowseCommand.java
index 8a7a6a9d8a7b0ce7a3a2c0aadbac0bb5508d2661..5bf87dcf9efd0862cfd045b813759bf03d63e661 100644
--- a/src/main/java/de/prob2/jupyter/commands/BrowseCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/BrowseCommand.java
@@ -1,17 +1,21 @@
 package de.prob2.jupyter.commands;
 
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import com.google.inject.Inject;
 
+import de.prob.animator.domainobjects.FormulaExpand;
 import de.prob.model.representation.AbstractElement;
-import de.prob.model.representation.BEvent;
 import de.prob.model.representation.Constant;
 import de.prob.model.representation.Set;
 import de.prob.model.representation.Variable;
 import de.prob.statespace.AnimationSelector;
 import de.prob.statespace.Trace;
+import de.prob.statespace.Transition;
 
 import de.prob2.jupyter.ProBKernel;
 
@@ -19,6 +23,8 @@ import io.github.spencerpark.jupyter.messages.DisplayData;
 
 import org.jetbrains.annotations.NotNull;
 
+import se.sawano.java.text.AlphanumericComparator;
+
 public final class BrowseCommand implements LineCommand {
 	private final @NotNull AnimationSelector animationSelector;
 	
@@ -65,8 +71,18 @@ public final class BrowseCommand implements LineCommand {
 		sb.append("\nVariables: ");
 		sb.append(elementsToString(mainComponent, Variable.class, Variable::getName));
 		sb.append("\nOperations: ");
-		sb.append(elementsToString(mainComponent, BEvent.class, Object::toString));
-		sb.append('\n');
+		final List<Transition> sortedTransitions = new ArrayList<>(trace.getNextTransitions(true, FormulaExpand.TRUNCATE));
+		// Transition IDs are strings, but they almost always contain numbers.
+		sortedTransitions.sort(Comparator.comparing(Transition::getId, new AlphanumericComparator()));
+		for (final Transition t : sortedTransitions) {
+			sb.append('\n');
+			sb.append(t.getId());
+			sb.append(": ");
+			sb.append(t.getRep());
+		}
+		if (trace.getCurrentState().isMaxTransitionsCalculated()) {
+			sb.append("\nMore operations may be available (MAX_OPERATIONS/MAX_INITIALISATIONS reached)");
+		}
 		return new DisplayData(sb.toString());
 	}
 }
diff --git a/src/main/java/de/prob2/jupyter/commands/ExecCommand.java b/src/main/java/de/prob2/jupyter/commands/ExecCommand.java
index 1ad66e57056764724985eddea4e390ea783c4333..384cacd8637992e4f1e9fbd1bd1b0e071b33598f 100644
--- a/src/main/java/de/prob2/jupyter/commands/ExecCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/ExecCommand.java
@@ -1,6 +1,7 @@
 package de.prob2.jupyter.commands;
 
 import java.util.List;
+import java.util.Optional;
 
 import com.google.inject.Inject;
 
@@ -26,25 +27,39 @@ public final class ExecCommand implements LineCommand {
 	
 	@Override
 	public @NotNull String getSyntax() {
-		return ":exec OPERATION [PREDICATE]";
+		return ":exec OPERATION [PREDICATE]\n:exec OPERATION_ID";
 	}
 	
 	@Override
 	public @NotNull String getShortHelp() {
-		return "Execute an operation with the specified predicate";
+		return "Execute an operation with the specified predicate, or by its ID";
 	}
 	
 	@Override
 	public @NotNull DisplayData run(final @NotNull ProBKernel kernel, final @NotNull String name, final @NotNull String argString) {
 		final String[] split = argString.split("\\h", 2);
 		assert split.length >= 1;
-		final String opName = split[0];
-		final String predicate = split.length >= 2 ? split[1] : "1=1";
+		final String opNameOrId = split[0];
+		
 		final Trace trace = this.animationSelector.getCurrentTrace();
-		final List<Transition> transitions = trace.getStateSpace().transitionFromPredicate(trace.getCurrentState(), opName, predicate, 1);
-		assert transitions.size() == 1;
-		final Transition op = transitions.get(0);
+		// Check if the argument is an ID, by searching for a transition with that ID.
+		final Optional<Transition> opt = trace.getNextTransitions().stream().filter(t -> t.getId().equals(opNameOrId)).findAny();
+		final Transition op;
+		if (opt.isPresent()) {
+			// Transition found, nothing else needs to be done.
+			if (split.length != 1) {
+				throw new CommandExecutionException(name, "Cannot specify a predicate when executing an operation by ID");
+			}
+			op = opt.get();
+		} else {
+			// Transition not found, assume that the argument is an operation name instead.
+			final String predicate = split.length >= 2 ? split[1] : "1=1";
+			final List<Transition> transitions = trace.getStateSpace().transitionFromPredicate(trace.getCurrentState(), opNameOrId, predicate, 1);
+			assert transitions.size() == 1;
+			op = transitions.get(0);
+		}
+		
 		this.animationSelector.changeCurrentAnimation(trace.add(op));
-		return new DisplayData(String.format("Executed operation %s", opName));
+		return new DisplayData(String.format("Executed operation %s", opNameOrId));
 	}
 }