Overview
Upstream PyMOL’s command line is a Python interpreter: type a command verb and it runs the command language; type anything else and it is evaluated as Python, withcmd in scope
(from pymol import cmd).
tenmol keeps that exact split, but swaps the scripting language. In tenmol the console is a
client-side JavaScript REPL running against the in-browser TypeScript engine
(packages/engine-ts/). The engine is the port’s PyMOL instance; it exposes the same
cmd.* surface, so the thesis holds:
You can run all the same PyMOL scripts — just write them in JavaScript instead of Python.
cmd.fragment('ala') is identical in both languages. Only the host language changes: for
loops, let, ===, and object literals for keyword arguments replace Python’s for … in,
assignment, ==, and foo=bar kwargs.
State persists across console lines exactly like a Python REPL: objects you load, selections
you name, and settings you change stay live for the next line.
This page documents only what the TypeScript engine actually implements today. Symbols that are
not yet ported reject with a
NotPorted error rather than silently no-op — see What’s not yet
ported.The console
A console line is dispatched byEngine.do() (packages/engine-ts/src/engine.ts). The rule:
- A PyMOL command verb runs the command language. If the line (split on
;and newlines) contains any verb in the recognized set, the whole line is parsed askeyword arg1, arg2, …and run through the command handlers. - Everything else is JavaScript. The line is evaluated as JS with
cmd,print,console, and one global per PyMOL command namespace in scope —editor,util,preset,movie,gui(and any other registered namespace). So a bare namespaced call likeeditor.attach_amino_acid('pk1', 'gly')orutil.cbag('all')dispatches through the engine instead of throwingReferenceError: editor is not defined; an unported namespace verb reports a cleanNotPortedrather than a JS error. A bare expression prints its value;print(...)/console.log(...)print to the console; errors are shown, not thrown. import …/from … import …lines are silent. These are internal plugin bootstraps (real PyMOL runs them in its interpreter); the port stays silent for them./expris the explicit JavaScript escape — a leading slash forces the rest of the line to run as JS even if it would otherwise look like a command.
Command language
JavaScript
cmd.<fn>(...) calls the engine synchronously and returns its value, so you can compose:
let, and multiple statements:
Python → JavaScript, side by side
Thecmd calls are the same. Only the surrounding language changes.
Key differences:
- Syntax is JavaScript. Braces and semicolons,
let/constfor variables,===for equality, C-styleforloops. - Keyword arguments become a trailing object. Python
object='m1'becomes{ object: 'm1' }as the last argument. (The engine reads kwargs from a trailing object for the handlers that accept them, e.g.read_pdbstrandfragment.) - String arguments stay quoted, selections included:
cmd.color('red', 'chain A'). printandconsole.logboth route to the console output, the same place Pythonprintwrites upstream.
The command language
The console recognizes these verbs directly (fromKNOWN_KEYWORDS in engine.ts). Syntax is
keyword arg1, arg2, comma-separated, exactly as in PyMOL. Multiple commands can be separated
by ; or newlines.
Any other verb-shaped line is treated as JavaScript instead (a bare line runs as JS; an
import line is silent plumbing).
The cmd API reference
Every symbol below is registered in Engine.register(). Call them as cmd.<name>(...) from
the console, or as command verbs where one exists. Signatures show tenmol JS argument order;
the PyMOL column is the Python equivalent.
Loading
Objects & selections
Representations
The reps that render in-browser (Mode G) are:lines, spheres, sticks,
nonbonded, nb_spheres.
Color
Camera
Command namespaces
Beyond the flatcmd.<verb> surface, whole PyMOL command namespaces are ported. Call them
as cmd.<ns>.<verb>(...) or, in a JavaScript console line, as the bare namespace global
(preset.pretty('all'), editor.attach_amino_acid('pk1', 'gly') — see The console).
Engine.commandNames() lists every registered symbol.
An unported namespace verb reports a clean
NotPorted (cmd.editor.combine_fragment: not ported by @tenmol/engine-ts yet), never a silent no-op. Progress per namespace is tracked in
docs/parity-dashboard.md.
Settings
The engine also answers a set of benign read defaults so the app’s panels (movie, scenes,
views, settings) render cleanly on the local engine — e.g.
get_frame, get_state,
count_frames, count_states, get_scene_list, get_type, get_version, get_renderer. These
return the values a fresh, empty PyMOL session would; they are reads only, not feature
implementations.The selection language
Selections are parsed bypackages/engine-ts/src/select/selector.ts. Case-insensitive.
Property selectors take +-grouped multi-values (name CA+CB); resi, index, and id
also take lo-hi ranges. * and ? are wildcards in property values. An empty selection
means all (as in PyMOL).
Property selectors
Keyword selectors
Operators & set selectors
What’s not yet ported
Everything outside the surface above rejects with aPymolError of type NotPorted (mirroring
the bridge’s NotAllowed) — never a silent no-op — so gaps are visible and the differential
parity suite catches them. Reps beyond the five listed above are treated as “nothing to draw”
in Mode G.
For the full picture of what is ported and how parity with real PyMOL is proven, see
The TypeScript engine port.