Rate of turn
The rate of turn in degrees per minute, as circling dots, a track bar and a readout.
On this page
Code
import { Priority, RateOfTurn, RotPosition, RotType, WatchCircleType } from "@bosunkit/instruments";
import { useEffect, useState, type CSSProperties, type Dispatch, type ReactElement, type SetStateAction } from "react";
export const description: string =
"Tacking up the channel on a live feed: the turn rate swings to starboard, settles, and swings to port, shown as " +
"a track bar colored by side with a readout, as a bar arc on the scale in the enhanced palette, and as circling " +
"dots.";
const PANEL: CSSProperties = {
display: "flex",
flexWrap: "wrap",
alignItems: "flex-end",
justifyContent: "center",
gap: 28,
width: "100%",
maxWidth: 660,
};
const FIGURE: CSSProperties = { display: "flex", flexDirection: "column", alignItems: "center", gap: 6, margin: 0 };
const CAPTION: CSSProperties = { fontSize: 13, color: "var(--text-dim)", textAlign: "center", maxWidth: 220 };
/** Bar arc, in degrees, that a full 60 degrees per minute fills on the scale view. */
const BAR_ARC_AT_FULL_RATE: number = 45;
/**
* Rate of turn in degrees per minute at a time in seconds: a tack to starboard, a steady leg, a tack to port and a
* steady leg, every forty seconds.
*/
function rateAt(time: number): number {
const phase: number = time % 40;
if (phase < 8) {
return 48 * Math.sin((Math.PI * phase) / 8);
}
if (phase >= 20 && phase < 28) {
return -42 * Math.sin((Math.PI * (phase - 20)) / 8);
}
return 1.5 * Math.sin(time * 1.3);
}
export default function Showcase(): ReactElement {
const [second, setSecond]: [number, Dispatch<SetStateAction<number>>] = useState<number>(0);
useEffect((): (() => void) => {
const interval: number = window.setInterval((): void => {
setSecond((previous: number): number => previous + 1);
}, 1000);
return (): void => {
window.clearInterval(interval);
};
}, []);
const rate: number = rateAt(second);
return (
<div style={PANEL}>
<figure style={FIGURE}>
<div style={{ width: 240, height: 240 }}>
<RateOfTurn
rateOfTurnDegreesPerMinute={rate}
hasTrackBar
rotMaxValue={60}
rotArcExtent={60}
rotPortStarboard
rotType={RotType.dots}
rotPosition={RotPosition.innerCircle}
watchCircleType={WatchCircleType.triple}
hasReadout
label="ROT"
unit="°/min"
fractionDigits={1}
/>
</div>
<figcaption style={CAPTION}>Track bar by side, inner dots and the readout</figcaption>
</figure>
<figure style={FIGURE}>
<div style={{ width: 170, height: 170 }}>
<RateOfTurn
rateOfTurnDegreesPerMinute={rate}
rotType={RotType.bar}
rotPosition={RotPosition.scale}
barStartAngle={0}
barEndAngle={(rate / 60) * BAR_ARC_AT_FULL_RATE}
rotAtZeroDeadband={1}
priority={Priority.enhanced}
watchCircleType={WatchCircleType.double}
/>
</div>
<figcaption style={CAPTION}>Bar arc on the scale, enhanced, zero below 1 degree</figcaption>
</figure>
<figure style={FIGURE}>
<div style={{ width: 130, height: 130 }}>
<RateOfTurn rateOfTurnDegreesPerMinute={rate} rotDotAnimationFactor={30} rotPortStarboard />
</div>
<figcaption style={CAPTION}>Dots by side, spun faster</figcaption>
</figure>
</div>
);
}
The rate-of-turn instrument shows how fast the vessel swings, in degrees per minute, positive to starboard. Dots
circle at a speed that follows the rate. A track bar adds the amount of the turn as an arc, and rotPortStarboard
colors it by side. It ports obc-rate-of-turn; the overview lists the other instruments.
Examples
Rate of turn
Code
import { RateOfTurn } from "@bosunkit/instruments";
import type { ReactElement } from "react";
export const description: string = "Dots that circle starboard at a speed set by a turn of 15 degrees per minute.";
export default function RateOfTurnExample(): ReactElement {
return (
<div style={{ width: 200, height: 200 }}>
<RateOfTurn rateOfTurnDegreesPerMinute={15} />
</div>
);
}
Port bar
Code
import { RateOfTurn, WatchCircleType } from "@bosunkit/instruments";
import type { ReactElement } from "react";
export const description: string = "A turn of 24 degrees per minute to port as a track bar in the port color.";
export default function PortBar(): ReactElement {
return (
<div style={{ width: 200, height: 200 }}>
<RateOfTurn
rateOfTurnDegreesPerMinute={-24}
hasTrackBar
rotPortStarboard
watchCircleType={WatchCircleType.double}
/>
</div>
);
}
Enhanced bar
Code
import { Priority, RateOfTurn, WatchCircleType } from "@bosunkit/instruments";
import type { ReactElement } from "react";
export const description: string =
"A turn of 36 degrees per minute to starboard as a track bar in the enhanced palette.";
export default function EnhancedBar(): ReactElement {
return (
<div style={{ width: 200, height: 200 }}>
<RateOfTurn
rateOfTurnDegreesPerMinute={36}
hasTrackBar
priority={Priority.enhanced}
watchCircleType={WatchCircleType.double}
/>
</div>
);
}
With readout
Code
import { RateOfTurn } from "@bosunkit/instruments";
import type { ReactElement } from "react";
export const description: string = "The turn as a number in the center, labeled ROT, beside the circling dots.";
export default function WithReadout(): ReactElement {
return (
<div style={{ width: 200, height: 200 }}>
<RateOfTurn rateOfTurnDegreesPerMinute={15} hasReadout />
</div>
);
}
Rate, direction and age
Supply degrees per minute with starboard positive. A heading difference is not a rate until the sampling interval and angular wrap have been accounted for in the data layer. Keep the current numeric value available so motion is not the only way to read the instrument.
The moving dots visualize the supplied rate; they do not establish that updates are arriving. Surface stale or invalid measurements beside the instrument and check the reduced-motion presentation. A report should identify the recorded interval and remain understandable from a static capture.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| rateOfTurnDegreesPerMinute | number | undefined | – | Measured rate of turn in degrees per minute (the maritime/AIS convention). Sign controls direction (positive = starboard/clockwise). |
| rotDotAnimationFactor | number | undefined | 18 | Visual amplification applied to the spinning dot animation. Default 18
keeps the legacy visual feel (≈1 rpm at 20°/min). |
| rotType | RotType | undefined | RotType.dots | ROT display: dots (spinning dots, default) or bar (arc bar with clipped dots). |
| rotPosition | RotPosition | undefined | RotPosition.scale | Track position: scale (outer ring, default here) or innerCircle. |
| priority | Priority | undefined | Priority.regular | Color priority: regular (gray palette, default) or enhanced (blue palette) for the dots, the bars and the track
bar. |
| barStartAngle | number | undefined | 0 | Start angle of the static bar arc (0° = 12 o'clock, clockwise). Bar mode only. |
| barEndAngle | number | undefined | 30 | End angle of the static bar arc. Bar mode only. |
| watchCircleType | WatchCircleType | undefined | WatchCircleType.single | Ring style matched to the surrounding instrument (e.g. triple in compass contexts). |
| rotPortStarboard | boolean | undefined | false | Colors the dots and bars by the direction of the turn: starboard colors for a positive rate, port colors for a
negative one. Default false. |
| rotAtZeroDeadband | number | undefined | ROT_ZERO_DEADBAND_DEG | Smallest bar, in degrees of arc, that is drawn as a bar; a shorter one is drawn as the zero marker. Default 0.05.
Bar mode only. |
| hasTrackBar | boolean | undefined | false | When true, shows a bar in the ring band from the twelve o'clock
position to the measured rate of turn, with a needle marker at its end
and sector tickmarks. Pair with watchCircleType="double" for the
banded face — or "triple" when rotPosition is innerCircle, so the
dots keep their inner track. Driven by rateOfTurnDegreesPerMinute
only. |
| rotMaxValue | number | undefined | 60 | Bar-extent reference value in **degrees per minute**: the track bar
reaches ±rotArcExtent when the measured ROT equals ±rotMaxValue.
Default 60 aligns with ES-TRIN 2025/1 Art. 3.02. |
| rotArcExtent | number | undefined | 60 | Arc extent of the track bar in degrees per direction. Default 60. |
| hasReadout | boolean | undefined | false | When true, shows a centered readout with the measured rate of turn.
Shows a dash while rateOfTurnDegreesPerMinute is unset. |
| label | string | undefined | "ROT" | Readout label. Default ROT. |
| unit | string | undefined | "DEG/min" | Readout unit. Default DEG/min. |
| fractionDigits | number | undefined | 0 | Number of fraction digits shown in the readout. Default 0. |
| className | string | undefined | – | Class names added to the instrument's root element. |
Related
- Compass: the same indicator on the heading card.
- Rudder: the angle that causes the turn.
- Data integrity: the states a value can be in, and what the crew does.