Docs
DSL basics
Understand scenes, entities, connections, constraints, and presentation.
The PhysLoom DSL describes a physical model, not an SVG. A model usually contains five layers of information: a scene, entities, connections, physical parameters, and presentation requests.
Scenes
Every model begins with scene:
scene inclined_plane
// entities and relationships follow
The scene name identifies the model. It does not automatically become the visible title.
Entities
Entities are objects in the model. Common entities include body, support, pulley, surface, spring, charge, and circuit elements.
load crate 8kg
support ceiling orientation=horizontal
surface ramp incline 30deg
spring S 120N/m
Keep identifiers short and unique. Reader-facing labels can be set separately, so an identifier does not need to contain a sentence.
Connections and paths
Connections state how objects relate. For common pulley systems, declaring the pulley attachments is enough:
load A 2kg
load B 3kg
pulley P
support ceiling
P <-> ceiling
The compiler derives a tangent rope route through P and includes it in the rope-length constraint. Declare a named rope path only when several ropes or an unusual route make the topology ambiguous.
Parameters and units
Numbers should carry units:
| Quantity | Example |
|---|---|
| Mass | mass=2kg |
| Distance | distance=0.5m |
| Angle | angle=30deg |
| Stiffness | stiffness=120N/m |
The compiler normalizes units before solving. Do not replace units with names or hide 1kg in a display label to avoid a model parameter.
For a conducting rod on rails, a field can occupy a finite interval:
field B magnetic 1T direction=into_page x_min=1.5m x_max=3.5m
Both endpoints measure distance from the left rail ends and must lie on the rails.
Outside this interval, B is zero. Omit both endpoints for a field covering the whole
apparatus. This boundary model currently supports one rod moving rightward in a
closed resistive loop, not arbitrary particle or wave boundaries.
Try the field-crossing case and change only x_max.
Define once, reuse explicitly
Use let for a numeric fact shared by different declarations. Reference it with $name:
scene matched_lamp
let supply = 6V
source V $supply
lamp L $supply 3W
V <-> L <-> V
Changing supply updates both the source voltage and the lamp’s rated voltage. To change only
the applied voltage, keep the lamp’s rating independent instead. A shared parameter describes
an intentional equality, not a physical coupling invented by the compiler.
References work in named properties, shorthand and action quantities. For example,
motor M rated_voltage=$supply no_load_speed=60rad/s resistance=1ohm uses the same fact.
The electric-hoist case includes a working cross-domain example.
Parameters belong to one scene, may be declared after their uses, and may alias another
parameter with let nominal = $supply. Duplicate definitions, unknown references and cycles
are errors. Values must be finite numbers or physical quantities; strings, macros, loops and
arithmetic expressions are not supported. The destination property’s dimension and range still
apply: a mass parameter cannot become a lens focal length. Keep same-dimension properties named.
$F explicitly references a defined numeric parameter. Bare F remains a symbolic unknown
where the physical model supports symbols. Entity names, quoted labels and colors are not
parameter substitutions. Formatting, sharing and source export preserve let and $name.
Property value types
A property name appears to the left of =. Its runtime language definition constrains the value on the right. Validation, completion, and shorthand parsing all read that same definition.
| Value type | Accepted input | Example |
|---|---|---|
Enum enum | One of the finite choices in the definition | state=closed |
Identifier identifier | An object name, named direction, or another reference | field=EM, direction=down |
Quantity quantity | A number with a unit of the expected dimension | mass=2kg, angle=30deg |
Quantity or symbol quantity-or-symbol | A concrete quantity or a symbol left unevaluated | wavelength=500nm, wavelength=lambda |
Unitless number number | A numeric value without a unit | amplitude=0.5 |
Text text | A plain identifier or quoted text | label="main lamp" |
Any value any | A value for an extension that has not narrowed its contract yet | Rarely needed directly |
An enum is closed while an identifier is open. The editor may suggest common identifiers, but those suggestions do not turn the property into an enum.
Entities across mechanics, optics, electricity, waves, thermal/fluid and modern physics support definition-owned shorthand. With one unassigned shorthand property, the value binds directly and is validated. With several candidates, the compiler uses the value type and physical dimension to find one unique match, so these forms are equivalent:
lamp L 6V 3W
lamp L 3W 6V
The same rule lets you write everyday models without repeating obvious property names:
body block 1kg
pendulum P 0.6m 10deg
lens L converging 10cm
object candle 4cm subject=candle
fluid water 1000kg/m3
reservoir hot 600K
lens L 10cm converging is equivalent. Named and shorthand values may be mixed;
the order of values does not assign their physical meaning. Only the shorthand
properties listed in the language reference are eligible, not every property.
In Studio, hover over a property value, or place the cursor inside it and press
F1, to see its bound property and description. For example, 6V in a lamp
declaration shows rated_voltage=6V; a $supply reference also shows its current
resolved value. Escape dismisses the explanation. Editing closes it so an old
value is not left visible. This reads the existing language definition, changes
no source, and does not guess bindings for invalid input.
Keep ambiguous facts named: a spring’s rest length and displacement both use
length units, as do rail length and separation. Static and kinetic friction are
both unitless. Write spring S 16N/m rest_length=1m displacement=0.2m, not
spring S 16N/m 1m 0.2m. Explicit name=value remains a normal DSL form.
Presentation requests
show object requests the useful default details for a declared object. It does not change the physical model or start an animation:
load cargo
rope R
show cargo R
For a rope, show R requests tension. Use show cargo.displacement or show cargo.force_diagram when the lesson needs a particular result. These details come from the same solution; hiding them does not alter the rope constraint or force balance. Objects are still drawn without show.
Keep an example’s selections on one line, such as show cargo R. Use the bare
object name when its defaults match the intended details; keep .feature for a
narrower selection. Do not add show when the default diagram already shows what
the lesson needs, or use .all merely to shorten the source.
Use show cargo.all to request all available details for that object, or combine
selections with spaces or commas: show cargo.all, R.all. Only details supported
by the current model and renderer appear; missing results are not invented and
animation is not started. After show cargo., the editor suggests cargo.all
and individual features. Hover over a selection for its meaning.
Emphasize objects with color
Add a color at the end of an entity to change its visual emphasis without changing its material or physical properties:
load cargo 20kg #gold
pulley M #blue
force effort 100N direction=down #red
The language reference lists the available colors from the runtime contract. Write an explicit named rope route only when separate rope segments need different colors.
Shorter facts, explicit physical meaning
Write a field’s type and strength without repeating property names:
field B magnetic 1T direction=into_page
field E electric 200V/m direction=right
1T maps to magnetic_strength, and 200V/m to electric_strength by unit.
The type and quantity can be reversed. Explicit properties remain valid.
A declaration can include the common on relationship:
force pull 1N direction=right on bar
body crate 1kg on ramp
These are fragments; declare bar and ramp in the same scene. The force declaration means
force pull 1N direction=right followed by pull on bar. Entity facts go before on;
relationship parameters such as position=1cm go after its target.
Other relationships use a separate structural line or a named physical model:
body ball 1kg
launch flight body=ball origin=ground speed=20m/s angle=45deg
object candle 4cm
imaging view object=candle optic=L object_distance=30cm
Declare ground and the optic L in the same scene. flight and view name the models;
their names must be unique among entities and models. The role names, such as body, origin,
object, and optic, belong to each model’s language definition. With a declared object and
lens, candle -> L object_distance=30cm expresses the imaging relationship structurally.
Rolling uses a named rolling model, and a trajectory lists repeating stages as
stages=(ramp, floor, S). See the complete model recipes.
One animation command
Every animation uses animate. duration is experiment time; speed is the viewing multiplier:
animate duration=3s speed=0.5
This runs three seconds of the experiment at half speed, taking six screen seconds.
speed=1 is normal speed; speed=2 is double speed. The default is 1.
Changing speed never changes trajectories, forces or physical readouts.
A scene-wide interval is supported for linear motion, small-angle pendulums, spring oscillators, the conducting-rod case, motor hoists and stacked blocks. Use one interval for one connected system; its bodies share one physical clock.
To watch a body slide until it travels one metre, you do not need its arrival time:
scene incline_arrival
surface ramp incline 12deg smooth
body crate 1kg on ramp
animate crate until crate.displacement == 1m speed=1
show crate.motion crate.forces
until stops at the first arrival. Here the body starts from rest and gravity
determines its acceleration, reaching 1 m after about 0.99 s. speed=0.5 only
halves viewing speed. Displacement is measured from the initial position,
positive downslope and negative upslope. 0m is already satisfied and stays
static; static friction or an unreachable direction produces an error.
Currently this condition supports one sliding or rolling body on an incline,
including rolling with or without slip; rolling endpoints are currently downslope only.
For horizontal linear motion, use animate car until car.displacement == 40m speed=1. Displacement is signed from the initial x, positive right; a reversing body stops at its first arrival, and unreachable endpoints produce an error. Two bodies share one clock: animate A until A.displacement == 60m speed=1 observes both until A first travels 60 m. Without until, animate A still ends at their meeting.
For fixed-axis rotation, animate wheel until wheel.turn == 360deg speed=1
stops after one accumulated revolution. turn is not the wrapped orientation;
torque determines the direction and arrival time.
For a small-angle pendulum released from rest, animate P until P.angle == 8deg speed=1
stops at the first arrival at 8° from downward equilibrium. The angle is absolute,
positive to the right, and must be within the release amplitude. animate P speed=1
still observes a complete natural cycle.
For an undamped free spring released from rest, animate weight until weight.offset == 0m speed=1
stops at equilibrium after a quarter period. offset is signed from equilibrium,
positive downward, unlike displacement measured from the starting position.
Only the single-spring case without a damper, external force or stiffness modulation supports this endpoint.
For the photoelectric effect, animate electron until electron.displacement == 10nm sets the electron’s observation distance after emission; the photon’s arrival time is also included. 0nm leaves the scene static. Models not listed here are not yet supported.
After until, completion offers supported quantities and units;
the endpoint can also reference a $parameter. Choose until or duration, not
both. Only optional speed follows the condition, not a prescribed trajectory.
Deleting the animation leaves the same initial position.
Name an object to observe a solved event or cycle, or specify a supported parameter endpoint:
animate P speed=0.5
animate candle object_distance=15cm duration=3s speed=1
animate R.free distance=1m duration=2s speed=1
These are three separate examples: one pendulum cycle, an object-distance change,
and prescribed rope-end travel. Without duration, the model determines the endpoint,
such as landing, meeting or a cycle. Meeting alone does not imply collision.
When physics determines an event duration, an explicit duration must agree with it;
it cannot force an earlier landing. Change speed to adjust viewing pace.
Spring response follows damping, external forces and stiffness modulation. Periodic
forcing currently uses steady state, not startup transients. A modulated spring has
no guaranteed cycle endpoint and needs an interval such as animate duration=3s speed=1.
For free, damped and driven spring motion, the displayed offset from equilibrium
follows the modeled trajectory rather than a linear estimate.
Fields, currents, waves and energy ledgers use the same command and speed control.
Static field and DC markers are illustrative progress, not moving particles: they reject
physical duration, and speed=1 uses their default presentation pace. Waves and AC
retain frequency-derived physical periods, so fast phenomena may need tiny multipliers
such as speed=1e-8. Heat-engine ledgers show a declared energy schedule, not a
simulated thermodynamic path.
Old actions and playback remain compatible with saved drafts; new content uses
animate and speed consistently.
When to specify layout
Let the layout engine find a clear, non-overlapping arrangement first. Add a layout hint only when the problem truly depends on a particular composition. Prefer relative intent such as “to the right” or “keep vertical” over absolute pixels tied to one screen.
The boundary matters: the model states physical facts; layout states how to see them clearly.
Continue with Model recipes to see the same structure used across different areas of physics.