Bloch simulator for NMR and MRI education.
-
Adjusted original Hanson version for GitHub Pages hosting: https://mzaiss.github.io/Bloch-Simulator/
-
Added new Mixed Matter(2) for simpler IR explanation, matching an educational Jupyter Notebook https://colab.research.google.com/drive/1TJN8GDrTkvTlweFGjkLvl_pGwu-2J9vm
- Replaced ES6 module imports with script tags for browser compatibility
- Downgraded THREE.js from v0.132.2 to v0.124.0 to maintain THREE.Geometry support
- Fixed OrbitControls reference and added dependency error checking
- Added a new Mixed matter with just two compartments
- THREE.js v0.124.0, OrbitControls v0.124.0, dat.gui v0.7.7
- jQuery v3.5.1, jQuery UI v1.12.1
GitHub Pages: Fork repository, enable Pages in settings, see https://mzaiss.github.io/Bloch-Simulator/
Local: serve the folder over HTTP (needed for example .seq files and ChartGPU), e.g. python3 -m http.server, then open the page. Opening index.html as a file still works for the 3D sim; use Load .seq to pick a sequence without a server.
Load a Pulseq .seq file (or pick an example). The sequence is shown as a ChartGPU seq.plot (same stacked RF / GX / GY / GZ / ADC view as anyfield). Play runs the waveforms on the Bloch simulator. Time is stretched so a short sequence is watchable; use the speed slider to go faster or slower.
Defaults: B0 rotating frame, B1 view (not torque/B1eff), and a circular lab floor so the rotating frame does not show a spinning rectangle. Sequences with gradients switch to the Plane sample on Play if the current scene has no spatial extent (Equilibrium, etc.), so Gx/Gy are visible across the spin sheet.
Example sequences (from MRTwin_pulseq BlochSimWeb):
seq/web1_FID.seq— block-pulse FID, flip angles 30°…360°seq/web2_SpinEcho_me.seq— 90° then a train of 180°sseq/web2_SpinEcho_sinc.seq— sinc-pulse spin echoseq/web3_FLASH_16.seq— 16-line FLASHseq/web4_RARE_16.seq— 16-echo RAREseq/web5_EPI_16.seq— 16-line EPI
Parser tests: node tests/pulseq.test.js
Mixed matter (3) (3 compartments):
- Blue: T1=3.1s, T2=2.5s, generic tissue (dR1=0.2, dR2=0.2)
- Green: T1=8.0s, T2=5.0s, no additional relaxation (dR1=0, dR2=0)
- White: T1=8.0s, T2=2.5s, T2-only relaxation (dR1=0, dR2=0.2)
Mixed matter (2) (2 compartments):
- White: T1=3.1s, T2=2.5s, tissue with relaxation (dR1=0.2, dR2=0.2)
- Green: T1=8.0s, T2=5.0s, no additional relaxation (dR1=0, dR2=0)
To properly add new tissue presets:
-
Define isochromat functions using existing color constants (white, green, blue, red):
function IsocTissue1() { let M0 = 1.0; return new Isoc(new THREE.Vector3(0, 0, M0), white, nullvec, nElem, true, dR1, dR2, M0, dRadius); }
-
Create Substances function (not wrapper with basicState reset):
scenes.Substances2 = function () { let isocs = {IsocArr: [IsocTissue1(), IsocTissue2()]}; isocs.IsocArr[0].dB0 = 0; // On-resonance isocs.IsocArr[1].dB0 = 0.02; // Slightly off-resonance return isocs; }
-
Add menu items:
- HTML:
<li id="Mytissue"><a class="PresetsAction">My Tissue</a></li> - buttonAction:
case "My Tissue" : state.Sample = "My Tissue"; trigSampleChange = true; break;
- HTML:
-
Add sampleChange case with reapplication prevention:
case 'My Tissue': if (state.Sample == 'My Tissue Internal') break; // Prevent reapplication state = Object.assign(state, scenes.Substances2()); state.T1 = 8; state.T2 = 5; // Set global relaxation times state.Sample = 'My Tissue Internal'; frameFixed = false; break;
-
Use predefined colors (white, green, blue, red) to avoid undefined errors
-
Calculate dR1/dR2 where effective T1 = 1/(1/T1_global + dR1), T2 = 1/(1/T2_global + dR2)
Original: Lars G. Hanson (larsh@drcmr.dk) License: GNU GPL v3.0