---
title: "Rotation Representation Converter"
author: "Dieter Olson"
description: "Interactive calculator converting between rotation matrices, quaternions, Euler angles, axis-angle, and exponential coordinates with 3D views."
date: "Date unverified"
date-source: "unverified"
css:
- ../css/machine-learning.css
format:
html:
toc: true
toc-depth: 2
code-fold: true
page-layout: full
categories:
- tools
---
```{=html}
<div class="page-header">
<h1 class="section-heading">Rotation Representation Converter</h1>
<p class="page-subtitle">Interactive calculator for converting between rotation matrices, quaternions, ZYX Euler angles, axis-angle, and exponential coordinates β with 3D rotation-axis illustration and step-by-step formulas.</p>
</div>
```
## Background
This tool converts a single orientation among five representations. It uses right-handed active rotations of column vectors, $v'=Rv$, and Hamilton quaternions ordered $(w,x,y,z)$. A matrix can also map body-frame coordinates into world-frame coordinates when those frames are explicitly declared. The illustrated axis passes through an arbitrary display origin; orientation alone does not identify a joint location or a physical screw axis line.
| Representation | Stored Values | Convention and Limits |
|---|---|---|
| $R\in SO(3)$ | 9 matrix entries | Rows entered left to right; $R^TR=I$, $\det R=1$ |
| Unit quaternion $(w,x,y,z)$ | 4 scalars | Nonzero input normalized; $q$ and $-q$ represent one rotation |
| Intrinsic ZYX $(\psi,\theta,\phi)$ | Yaw, pitch, roll | $R=R_z(\psi)R_y(\theta)R_x(\phi)$; singular at pitch $\pm\pi/2$ |
| Axis-angle $(n,\theta)$ | 3 axis components and angle | Nonzero axis normalized; identity axis undefined |
| Rotation vector $\rho=\theta n$ | 3 components, always radians | $[\rho]_\times\in\mathfrak{so}(3)$; not angular velocity |
The degree/radian switch affects Euler and axis-angle fields. Rotation-vector components always remain radians, as their labels specify. Returned axis-angle and rotation-vector values use a principal angle in $[0,\pi]$; they do not retain accumulated revolutions. An active input can retain an equivalent nonprincipal description while other panels show the principal one.
::: {.callout-note}
## Provenance
The frame and exponential conventions are developed in Kevin M. Lynch and Frank C. Park's *Modern Robotics* (2017); see its official [rotation-exponential explanation](https://modernrobotics.northwestern.edu/nu-gm-book-resource/3-2-3-exponential-coordinates-of-rotation-part-2-of-2/). This tool's numerical branches, tolerances and normalization are specified below and independently tested. The [paired reference](rotation-representations-reference.html) derives the mathematics, supplies a checked Python example and connects orientation to golf mechanics. Its Python matrix tolerance is stricter than the browser's rounded-input tolerance; the implementations are not claimed byte-identical.
:::
### Reading the Results
Enter complete finite decimal numbers, including scientific notation such as `1e-8`. Invalid or incomplete input retains the last valid results with an explicit message. A zero axis, zero quaternion, reflection or materially nonorthogonal matrix is rejected. A unit quaternion or axis is normalized even if the active fields retain your original scale.
Matrix acceptance requires absolute determinant error and maximum absolute entry of $R^TR-I$ both below $10^{-5}$. This accommodates rounded entries. It is not an induced matrix infinity norm, a measurement confidence interval, or proof that noisy data are physically valid. Normalized outputs can differ slightly from an accepted approximate matrix. No nearest-rotation fitting algorithm is promised.
Try identity, a half-turn, and gimbal lock: the formula panel explains each branch. The 3-D frames share a display origin and unitless lengths. They illustrate orientation, not club translation, segment dimensions or measured motion. If WebGL is unavailable, every numerical result remains usable.
---
## Interactive Calculator
```{=html}
<div id="rc-app" style="font-family: system-ui, sans-serif;">
<!-- ββ Top bar: degree/radian toggle βββββββββββββββββββββββββββββββββββββββ -->
<div id="rc-toolbar" style="
display: flex; align-items: center; gap: 1rem;
margin-bottom: 1.25rem; padding: 0.6rem 1rem;
background: var(--bg-secondary,#f3f4f6); border-radius: 8px;
border: 1px solid var(--border-color,#e5e7eb);
">
<span style="font-weight:600; color:var(--text-primary,#111);">Angle units:</span>
<label style="display:flex;align-items:center;gap:0.4rem;cursor:pointer;">
<input type="radio" name="rc-units" value="deg" checked id="rc-deg"> Degrees
</label>
<label style="display:flex;align-items:center;gap:0.4rem;cursor:pointer;">
<input type="radio" name="rc-units" value="rad" id="rc-rad"> Radians
</label>
<span style="margin-left:auto;font-size:0.85rem;color:var(--text-secondary,#475569);">
Edit any field to update all representations
</span>
</div>
<!-- ββ Status bar ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ -->
<div id="rc-status" role="status" aria-live="polite" style="
display:none; padding:0.5rem 1rem; border-radius:6px;
margin-bottom:1rem; font-size:0.9rem; font-weight:500;
"></div>
<!-- ββ Two-column main panels βββββββββββββββββββββββββββββββββββββββββββββββ -->
<div class="rc-main-grid">
<!-- Left column ββ -->
<div style="display:flex;flex-direction:column;gap:1rem;">
<!-- Rotation Matrix -->
<fieldset id="rc-panel-R" class="rc-panel" style="border:2px solid #e5e7eb;border-radius:10px;padding:1rem;">
<legend style="font-weight:700;padding:0 0.5rem;">
Rotation Matrix <span style="font-weight:400;font-size:0.85rem;color:var(--text-secondary,#475569);">$R \in SO(3)$</span>
</legend>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:0.35rem;margin-bottom:0.6rem;">
<input class="rc-matrix-cell" data-row="0" data-col="0" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="1" aria-label="R row 1 col 1">
<input class="rc-matrix-cell" data-row="0" data-col="1" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" aria-label="R row 1 col 2">
<input class="rc-matrix-cell" data-row="0" data-col="2" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" aria-label="R row 1 col 3">
<input class="rc-matrix-cell" data-row="1" data-col="0" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" aria-label="R row 2 col 1">
<input class="rc-matrix-cell" data-row="1" data-col="1" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="1" aria-label="R row 2 col 2">
<input class="rc-matrix-cell" data-row="1" data-col="2" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" aria-label="R row 2 col 3">
<input class="rc-matrix-cell" data-row="2" data-col="0" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" aria-label="R row 3 col 1">
<input class="rc-matrix-cell" data-row="2" data-col="1" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" aria-label="R row 3 col 2">
<input class="rc-matrix-cell" data-row="2" data-col="2" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="1" aria-label="R row 3 col 3">
</div>
<div id="rc-R-validity" style="font-size:0.82rem;color:var(--text-secondary,#475569);margin-bottom:0.5rem;"></div>
<button type="button" class="rc-load-btn" data-rep="R" style="font-size:0.85rem;">β Use this R</button>
<div class="rc-formula-box" id="rc-formula-R" style="display:none;"></div>
</fieldset>
<!-- Quaternion -->
<fieldset id="rc-panel-q" class="rc-panel" style="border:2px solid #e5e7eb;border-radius:10px;padding:1rem;">
<legend style="font-weight:700;padding:0 0.5rem;">
Quaternion <span style="font-weight:400;font-size:0.85rem;color:var(--text-secondary,#475569);">$(w, x, y, z)$</span>
</legend>
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:0.35rem;margin-bottom:0.6rem;">
<div><label style="font-size:0.78rem;color:var(--text-secondary,#475569);display:block;" for="rc-q-w">w (scalar)</label>
<input class="rc-q-cell" data-comp="0" id="rc-q-w" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="1" style="width:100%;"></div>
<div><label style="font-size:0.78rem;color:var(--text-secondary,#475569);display:block;" for="rc-q-x">x</label>
<input class="rc-q-cell" data-comp="1" id="rc-q-x" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" style="width:100%;"></div>
<div><label style="font-size:0.78rem;color:var(--text-secondary,#475569);display:block;" for="rc-q-y">y</label>
<input class="rc-q-cell" data-comp="2" id="rc-q-y" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" style="width:100%;"></div>
<div><label style="font-size:0.78rem;color:var(--text-secondary,#475569);display:block;" for="rc-q-z">z</label>
<input class="rc-q-cell" data-comp="3" id="rc-q-z" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" style="width:100%;"></div>
</div>
<div id="rc-q-validity" style="font-size:0.82rem;color:var(--text-secondary,#475569);margin-bottom:0.5rem;"></div>
<button type="button" class="rc-load-btn" data-rep="quaternion">β Use this q</button>
<div class="rc-formula-box" id="rc-formula-q" style="display:none;"></div>
</fieldset>
<!-- ZYX Euler -->
<fieldset id="rc-panel-euler" class="rc-panel" style="border:2px solid #e5e7eb;border-radius:10px;padding:1rem;">
<legend style="font-weight:700;padding:0 0.5rem;">
ZYX Euler Angles <span style="font-weight:400;font-size:0.85rem;color:var(--text-secondary,#475569);">$R = R_z(\psi)R_y(\theta)R_x(\phi)$</span>
</legend>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:0.5rem;margin-bottom:0.6rem;">
<div><label style="font-size:0.78rem;color:var(--text-secondary,#475569);display:block;" id="rc-lbl-psi" for="rc-euler-psi">Yaw Ο (Β°)</label>
<input class="rc-euler-cell" data-comp="psi" id="rc-euler-psi" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" style="width:100%;"></div>
<div><label style="font-size:0.78rem;color:var(--text-secondary,#475569);display:block;" id="rc-lbl-theta" for="rc-euler-theta">Pitch ΞΈ (Β°)</label>
<input class="rc-euler-cell" data-comp="theta" id="rc-euler-theta" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" style="width:100%;"></div>
<div><label style="font-size:0.78rem;color:var(--text-secondary,#475569);display:block;" id="rc-lbl-phi" for="rc-euler-phi">Roll Ο (Β°)</label>
<input class="rc-euler-cell" data-comp="phi" id="rc-euler-phi" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" style="width:100%;"></div>
</div>
<div id="rc-euler-validity" style="font-size:0.82rem;color:var(--text-secondary,#475569);margin-bottom:0.5rem;"></div>
<button type="button" class="rc-load-btn" data-rep="eulerZYX">β Use these angles</button>
<div class="rc-formula-box" id="rc-formula-euler" style="display:none;"></div>
</fieldset>
</div><!-- end left column -->
<!-- Right column ββ -->
<div style="display:flex;flex-direction:column;gap:1rem;">
<!-- Axis-Angle -->
<fieldset id="rc-panel-aa" class="rc-panel" style="border:2px solid #e5e7eb;border-radius:10px;padding:1rem;">
<legend style="font-weight:700;padding:0 0.5rem;">
Axis-Angle <span style="font-weight:400;font-size:0.85rem;color:var(--text-secondary,#475569);">$(\hat{\mathbf{n}}, \theta)$</span>
</legend>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1.5fr;gap:0.35rem;margin-bottom:0.6rem;">
<div><label style="font-size:0.78rem;color:var(--text-secondary,#475569);display:block;" for="rc-aa-nx">nβ</label>
<input class="rc-aa-cell" data-comp="nx" id="rc-aa-nx" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" style="width:100%;"></div>
<div><label style="font-size:0.78rem;color:var(--text-secondary,#475569);display:block;" for="rc-aa-ny">n_y</label>
<input class="rc-aa-cell" data-comp="ny" id="rc-aa-ny" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" style="width:100%;"></div>
<div><label style="font-size:0.78rem;color:var(--text-secondary,#475569);display:block;" for="rc-aa-nz">n_z</label>
<input class="rc-aa-cell" data-comp="nz" id="rc-aa-nz" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="1" style="width:100%;"></div>
<div><label class="rc-aa-angle-lbl" style="font-size:0.78rem;color:var(--text-secondary,#475569);display:block;" for="rc-aa-angle">ΞΈ (Β°)</label>
<input class="rc-aa-cell" data-comp="angle" id="rc-aa-angle" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" style="width:100%;"></div>
</div>
<div id="rc-aa-validity" style="font-size:0.82rem;color:var(--text-secondary,#475569);margin-bottom:0.5rem;"></div>
<button type="button" class="rc-load-btn" data-rep="axisAngle">β Use this axis-angle</button>
<div class="rc-formula-box" id="rc-formula-aa" style="display:none;"></div>
</fieldset>
<!-- Exponential Coordinates -->
<fieldset id="rc-panel-exp" class="rc-panel" style="border:2px solid #e5e7eb;border-radius:10px;padding:1rem;">
<legend style="font-weight:700;padding:0 0.5rem;">
Exponential Coordinates <span style="font-weight:400;font-size:0.85rem;color:var(--text-secondary,#475569);">$\rho = \theta\hat{\mathbf{n}}$</span>
</legend>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:0.35rem;margin-bottom:0.6rem;">
<div><label style="font-size:0.78rem;color:var(--text-secondary,#475569);display:block;" for="rc-exp-0">Οβ (rad)</label>
<input class="rc-exp-cell" data-comp="0" id="rc-exp-0" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" style="width:100%;"></div>
<div><label style="font-size:0.78rem;color:var(--text-secondary,#475569);display:block;" for="rc-exp-1">Ο_y (rad)</label>
<input class="rc-exp-cell" data-comp="1" id="rc-exp-1" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" style="width:100%;"></div>
<div><label style="font-size:0.78rem;color:var(--text-secondary,#475569);display:block;" for="rc-exp-2">Ο_z (rad)</label>
<input class="rc-exp-cell" data-comp="2" id="rc-exp-2" type="text" inputmode="decimal" autocorrect="off" autocapitalize="none" spellcheck="false" value="0" style="width:100%;"></div>
</div>
<div id="rc-exp-validity" style="font-size:0.82rem;color:var(--text-secondary,#475569);margin-bottom:0.5rem;"></div>
<button type="button" class="rc-load-btn" data-rep="expCoord">β Use these coords</button>
<div class="rc-formula-box" id="rc-formula-exp" style="display:none;"></div>
</fieldset>
<!-- Rotation Properties summary -->
<fieldset style="border:2px solid #d1fae5;border-radius:10px;padding:1rem;background:var(--bg-secondary,#f0fdf4);">
<legend style="font-weight:700;padding:0 0.5rem;">Properties</legend>
<table style="width:100%;font-size:0.88rem;border-collapse:collapse;">
<tr><td style="color:var(--text-secondary,#374151);padding:0.2rem 0.5rem 0.2rem 0;">Rotation angle ΞΈ:</td>
<td id="rc-prop-angle" style="font-weight:600;font-family:monospace;">β</td></tr>
<tr><td style="color:var(--text-secondary,#374151);padding:0.2rem 0.5rem 0.2rem 0;">Rotation axis nΜ:</td>
<td id="rc-prop-axis" style="font-weight:600;font-family:monospace;">β</td></tr>
<tr><td style="color:var(--text-secondary,#374151);padding:0.2rem 0.5rem 0.2rem 0;">det(R):</td>
<td id="rc-prop-det" style="font-weight:600;font-family:monospace;">β</td></tr>
<tr><td style="color:var(--text-secondary,#374151);padding:0.2rem 0.5rem 0.2rem 0;">Maximum Entry of |Rα΅R β I|:</td>
<td id="rc-prop-orth" style="font-weight:600;font-family:monospace;">β</td></tr>
<tr><td style="color:var(--text-secondary,#374151);padding:0.2rem 0.5rem 0.2rem 0;">βqβ:</td>
<td id="rc-prop-qnorm" style="font-weight:600;font-family:monospace;">β</td></tr>
<tr id="rc-row-gimbal" style="display:none;">
<td colspan="2" id="rc-prop-gimbal" style="
color:#92400e;background:#fef3c7;border-radius:4px;
padding:0.3rem 0.5rem;font-size:0.82rem;font-weight:600;
"></td></tr>
</table>
</fieldset>
<!-- Preset rotations -->
<fieldset style="border:2px solid #e5e7eb;border-radius:10px;padding:1rem;">
<legend style="font-weight:700;padding:0 0.5rem;">Presets</legend>
<div style="display:flex;flex-wrap:wrap;gap:0.4rem;">
<button type="button" class="rc-preset-btn" data-preset="identity">Identity</button>
<button type="button" class="rc-preset-btn" data-preset="rx90">Rx(90Β°)</button>
<button type="button" class="rc-preset-btn" data-preset="ry90">Ry(90Β°)</button>
<button type="button" class="rc-preset-btn" data-preset="rz90">Rz(90Β°)</button>
<button type="button" class="rc-preset-btn" data-preset="rz180">Rz(180Β°)</button>
<button type="button" class="rc-preset-btn" data-preset="arbitrary">nΜ=[1,1,1]/β3, 47.3Β°</button>
<button type="button" class="rc-preset-btn" data-preset="gimbal">Gimbal lock (ΞΈ=90Β°)</button>
</div>
</fieldset>
</div><!-- end right column -->
</div><!-- end grid -->
<!-- ββ Formula panel ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ -->
<details id="rc-formula-panel" style="margin-top:1.25rem;">
<summary style="
font-weight:700; font-size:1rem; cursor:pointer;
padding:0.6rem 1rem; color:var(--text-primary,#212529); background:var(--bg-secondary,#f3f4f6);
border:1px solid var(--border-color,#e5e7eb); border-radius:8px;
">Step-by-Step Conversion Formulas</summary>
<div id="rc-formula-content" style="
margin-top:0.5rem; padding:1rem;
border:1px solid var(--border-color,#e5e7eb); border-radius:8px;
background:var(--bg-primary,#fff); overflow-x:auto;
">
<div id="rc-formula-text" style="font-size:0.92rem; line-height:1.6;"></div>
</div>
</details>
<!-- ββ 3-D Screw Axis Visualiser βββββββββββββββββββββββββββββββββββββββββββββ -->
<details id="rc-viz-details" open style="margin-top:1.25rem;">
<summary style="
font-weight:700; font-size:1rem; cursor:pointer;
padding:0.6rem 1rem; color:var(--text-primary,#212529); background:var(--bg-secondary,#f3f4f6);
border:1px solid var(--border-color,#e5e7eb); border-radius:8px;
">3-D Rotation Axis Visualisation</summary>
<div style="
margin-top:0.5rem; padding:0.75rem;
border:1px solid var(--border-color,#e5e7eb); border-radius:8px;
background:#0a0a14;
">
<div style="
display:flex; gap:0.75rem; flex-wrap:wrap;
margin-bottom:0.5rem; font-size:0.82rem; color:#bbb;
">
<span>π± Left-drag: orbit</span>
<span>π± Scroll: zoom</span>
<span>π± Right-drag: pan</span>
<span id="rc-viz-info" style="margin-left:auto;color:#fbbf24;"></span>
</div>
<canvas role="img" aria-label="Reference and rotated coordinate frames; numerical axis and angle are provided in the Properties panel" id="rc-canvas" style="width:100%;height:450px;display:block;border-radius:6px;"></canvas>
<div id="rc-webgl-fallback" style="display:none; padding:1rem; color:#e5e7eb; text-align:center;">
WebGL not available β see Properties panel above for numeric results.
</div>
</div>
</details>
</div><!-- end #rc-app -->
<link rel="stylesheet" href="../css/rotation-converter.css">
<!-- ββ Scripts ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
rotation-converter.js is the pure math engine (no DOM dependencies).
Three.js and OrbitControls are loaded from CDN for the 3-D visualiser.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ -->
<script src="../js/rotation-converter.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.147.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.147.0/examples/js/controls/OrbitControls.js"></script>
<script src="../js/rotation-converter-viz.js"></script>
<script src="../js/rotation-converter-ui.js"></script>
```
---
## Mathematical Reference
### Rodrigues Formula (Axis-Angle β R)
For unit axis $n$ and angle $\theta$,
$$R=I+\sin\theta[n]_\times+2\sin^2(\theta/2)[n]_\times^2,$$
$$[n]_\times=\begin{bmatrix}0&-n_z&n_y\\n_z&0&-n_x\\-n_y&n_x&0\end{bmatrix}.$$
The half-angle coefficient equals $1-\cos\theta$ without subtracting nearly equal numbers for tiny angles. The implementation constructs the equivalent unit quaternion and then its rotation matrix. It retains small nonzero rotations rather than snapping them to identity.
### Rotation Matrix β Axis-Angle
The trace identity $\cos\theta=(\operatorname{tr}R-1)/2$ is exact, but arccos loses precision near zero and $\pi$. Instead, after stable quaternion extraction and choosing $w\geq0$,
$$\theta=2\operatorname{atan2}(\|v\|,w),\qquad n=\frac{v}{\|v\|}\quad(\|v\|>0).$$
At identity the axis is undefined; the numeric panel uses the conventional $z$ axis and the illustration hides its axis arrow. At exactly $\pi$, either axis sign gives the same orientation. Away from that half-turn, changing axis sign while retaining a positive principal angle generally changes the rotation.
### Rotation Matrix β Quaternion {#rotation-matrix-quaternion-shepperd}
Use **largest-component extraction** to avoid dividing by a small quaternion component. Compute
$$4(w^2,x^2,y^2,z^2)=
(1+\operatorname{tr}R,\ 1+2R_{11}-\operatorname{tr}R,\ 1+2R_{22}-\operatorname{tr}R,\ 1+2R_{33}-\operatorname{tr}R).$$
Choose the largest squared component, take its positive square root, and recover the others from the symmetric and antisymmetric matrix entries. For example, if $x$ is selected,
$$w=\frac{R_{32}-R_{23}}{4x},\qquad y=\frac{R_{12}+R_{21}}{4x},\qquad z=\frac{R_{13}+R_{31}}{4x}.$$
For an exact rotation the selected component has magnitude at least $1/2$, avoiding a vanishing denominator. Normalize the result and, if necessary, negate all components to choose $w\geq0$. This convention makes the reported angle principal; it is not a continuity rule for a sampled swing.
### Quaternion β Rotation Matrix
For a normalized Hamilton quaternion,
$$R=\begin{bmatrix}
1-2(y^2+z^2)&2(xy-wz)&2(xz+wy)\\
2(xy+wz)&1-2(x^2+z^2)&2(yz-wx)\\
2(xz-wy)&2(yz+wx)&1-2(x^2+y^2)
\end{bmatrix}.$$
$q$ and $-q$ produce the same matrix. Scalar-last software requires explicit reordering. Normalization rescales before taking the norm, so large finite quaternion entries need not overflow the calculation.
### ZYX Euler Angles
With $R=R_z(\psi)R_y(\theta)R_x(\phi)$, generic extraction is
$$\psi=\operatorname{atan2}(R_{21},R_{11}),\qquad \phi=\operatorname{atan2}(R_{32},R_{33}),$$
$$\theta=\operatorname{atan2}\!\left(-R_{31},\sqrt{R_{11}^2+R_{21}^2}\right).$$
At exact pitch $+\pi/2$, only $\psi-\phi$ is determined; at $-\pi/2$, only $\psi+\phi$ is determined. The implementation uses $|\cos\theta|<10^{-12}$ as its numerical lock threshold and returns $\phi=0$, $\psi=\operatorname{atan2}(-R_{12},R_{22})$. This represents the same orientation within the numerical threshold. Near lock, small orientation perturbations can cause large changes in the extracted yaw and roll; a singular coordinate chart does not remove the body's ability to rotate.
### Exponential Coordinates
$$\rho=\theta n\in\mathbb R^3,\qquad [\rho]_\times\in\mathfrak{so}(3),\qquad R=\exp([\rho]_\times).$$
The vector describes one equivalent finite rotation. It does not locate the axis in space or identify angular velocity. For time-dependent orientation, $[\omega_s]_\times=\dot R R^T$ and $[\omega_b]_\times=R^T\dot R$; generally $\dot\rho\ne\omega_s,\omega_b$. The [reference](rotation-representations-reference.html#exponential-coordinates-lie-algebra-mathfrakso3) derives the rate mapping and its assumptions.
### What the Converter Can Establish About a Swing
Matching reconstructed matrices establishes that two representations describe the same orientation within tolerance. It cannot establish equal segment velocities, the same motion history or equal kinetic energy. Relative pelvis/thorax orientation needs calibrated frames; clubface-direction sensitivity needs an angular-velocity or perturbation model; energy transfer needs inertia, forces, application-point velocities and a work ledger. The [reference's golf section](rotation-representations-reference.html#from-orientation-to-golf-mechanics) connects these quantities explicitly.
A useful consistency check is to convert a chosen orientation into each representation and back, including near zero, $\pi$ and Euler lock. Compare matrices or their action on basis vectors, not raw quaternion signs or wrapped angles. Then apply the same checks to your declared sensor and model conventions before interpreting a segment sequence mechanically.
## Related Articles
- [Rotation Representations Reference](rotation-representations-reference.html)
- [Screw Theory Reference](screw-theory-reference.html)
- [The Geometry of Motion, Rotations and SE(3)](The_Geometry_of_Motion/quarto/vol0_ch04_rotations_se3.html)
- [Programs & Tools](../pages/tools.html)