-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
929 lines (843 loc) · 63 KB
/
index.html
File metadata and controls
929 lines (843 loc) · 63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, shrink-to-fit=no, interactive-widget=resizes-content">
<title>N-Body Simulation</title>
<link rel="manifest" href="manifest.json">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="theme-color" content="#1c1c1e">
<meta name="description" content="A real-time interactive N-Body physics simulation. Explore gravity, electromagnetism, and orbital mechanics with customizable bodies and fields directly in your browser.">
<meta name="keywords" content="N-Body, Physics Simulation, Gravity, Orbital Mechanics, Astronomy, JavaScript, Canvas, Interactive, Science, Education, Electromagnetism">
<meta name="author" content="Wartets">
<meta name="robots" content="index, follow">
<meta property="og:site_name" content="N-Body Simulation">
<meta property="og:type" content="website">
<meta property="og:url" content="https://wartets.github.io/N-Body-Simulation/">
<meta property="og:title" content="N-Body Simulation">
<meta property="og:description" content="Simulate complex gravitational and electromagnetic interactions in real-time. Create solar systems, galaxies, or particle experiments.">
<meta property="og:image" content="https://wartets.github.io/N-Body-Simulation/app-icon.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:url" content="https://wartets.github.io/N-Body-Simulation/">
<meta name="twitter:title" content="N-Body Simulation">
<meta name="twitter:description" content="Simulate complex gravitational and electromagnetic interactions in real-time. Create solar systems, galaxies, or particle experiments.">
<meta name="twitter:image" content="https://wartets.github.io/N-Body-Simulation/app-icon.jpg">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"></script>
<link rel="icon" href="https://wartets.github.io/N-Body-Problem/image/browser-icon.png">
<link rel="apple-touch-icon" href="https://wartets.github.io/N-Body-Problem/image/browser-icon.png">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="simCanvas"></canvas>
<div id="controlPanel">
<div id="panelHeader">
<span class="app-title"><i class="fa-solid fa-sliders"></i> Settings</span>
<button id="togglePanelBtn"><i class="fa-solid fa-minus"></i></button>
</div>
<div class="tab-bar">
<button class="tab-btn active" data-tab="simulation" title="Simulation" data-inactive-class="fa-flask" data-active-class="fa-flask-vial"><i class="fa-solid fa-flask-vial"></i></button>
<button class="tab-btn" data-tab="view" title="View" data-inactive-class="fa-eye" data-active-class="fa-video"><i class="fa-solid fa-video"></i></button>
<button class="tab-btn" data-tab="bodies" title="Bodies" data-inactive-class="fa-circle-dot" data-active-class="fa-bullseye"><i class="fa-solid fa-circle-dot"></i></button>
<button class="tab-btn" data-tab="tools" title="Tools" data-inactive-class="fa-toolbox" data-active-class="fa-wrench"><i class="fa-solid fa-wrench"></i></button>
<button class="tab-btn" data-tab="infos" title="Infos" data-inactive-class="fa-book" data-active-class="fa-book-open"><i class="fa-solid fa-book"></i></button>
</div>
<div id="panelContent">
<div id="tab-simulation" class="tab-pane active">
<div class="control-section">
<div class="section-header">Simulation control</div>
<div class="btn-group">
<button id="playPauseBtn" class="btn primary">
<i class="fa-solid fa-pause"></i> Pause
</button>
<button id="resetBtn" class="btn danger">
<i class="fa-solid fa-rotate-right"></i> Reset
</button>
</div>
<div class="control-row" style="margin-top: 8px;">
<label>Time step dt <i class="fa-solid fa-circle-question help-icon" data-tooltip="Simulation time step. Higher values speed up simulation but may reduce accuracy."></i></label>
<span id="dtVal" class="value-display"></span>
</div>
<input type="range" id="dtSlider" min="0" max="1000" step="1">
<div class="divider-sub"></div>
<div class="section-header-2" style="margin-top: 8px;">Interactions</div>
<label class="toggle-row">
<span>Gravity <i class="fa-solid fa-circle-question help-icon" data-tooltip="Enables gravitational attraction between massive bodies."></i></span>
<input type="checkbox" id="gravBox" checked>
<div class="toggle-switch"></div>
</label>
<label class="toggle-row" style="margin-top:4px;">
<span>Electricity <i class="fa-solid fa-circle-question help-icon" data-tooltip="Enables attraction/repulsion forces between charged bodies."></i></span>
<input type="checkbox" id="elecBox">
<div class="toggle-switch"></div>
</label>
<label class="toggle-row" style="margin-top:4px;">
<span>Magnetism <i class="fa-solid fa-circle-question help-icon" data-tooltip="Enables magnetic forces based on body magnetic moments."></i></span>
<input type="checkbox" id="magBox">
<div class="toggle-switch"></div>
</label>
<label class="toggle-row" style="margin-top:4px;">
<span>Collisions <i class="fa-solid fa-circle-question help-icon" data-tooltip="Enables detection and resolution of physical collisions between bodies."></i></span>
<input type="checkbox" id="colBox">
<div class="toggle-switch"></div>
</label>
<label class="toggle-row" style="margin-top:4px;">
<span>Thermodynamics <i class="fa-solid fa-circle-question help-icon" data-tooltip="Enables heat generation from collisions and radiative cooling. Affects body properties."></i></span>
<input type="checkbox" id="thermoBox">
<div class="toggle-switch"></div>
</label>
<div id="thermoParams" class="hidden-content" style="margin-top: 8px;">
<div class="control-row">
<label>Ambient Temperature (K) <i class="fa-solid fa-circle-question help-icon" data-tooltip="Ambient temperature for radiative cooling. -1 for absence of temperature."></i></label>
<input type="text" id="ambientTempInput" style="width: 60px; text-align: right; background: var(--input-bg); border: 1px solid var(--input-border); color: var(--text-primary); border-radius: 3px; font-size: 11px; padding: 2px 4px;">
</div>
</div>
<label class="toggle-row" style="margin-top:4px;">
<span>Fragmentation <i class="fa-solid fa-circle-question help-icon" data-tooltip="Enables body fragmentation from high-energy collisions and tidal forces based on integrity."></i></span>
<input type="checkbox" id="fragBox">
<div class="toggle-switch"></div>
</label>
<div id="fragParams" class="hidden-content" style="margin-top: 8px;">
<div class="control-row">
<label>Fragments Lifetime <i class="fa-solid fa-circle-question help-icon" data-tooltip="Lifetime of fragments in ticks. -1 for infinite."></i></label>
<input type="text" id="fragLifetimeInput" style="width: 60px; text-align: right; background: var(--input-bg); border: 1px solid var(--input-border); color: var(--text-primary); border-radius: 3px; font-size: 11px; padding: 2px 4px;">
</div>
</div>
<div class="divider-sub"></div>
<div class="input-wrapper">
<label style="margin-bottom: 8px;">Global Preset <i class="fa-solid fa-circle-question help-icon" data-tooltip="Loads a complete simulation configuration (bodies, zones, parameters)."></i></label>
<div style="display:flex; gap:5px;">
<select id="simPresetSelect" style="flex:1;">
</select>
<button id="loadSimPresetBtn" class="btn secondary" style="width:auto;" title="Load Preset"><i class="fa-solid fa-upload"></i></button>
</div>
</div>
</div>
<div class="tool-group" style="margin-top: 10px;">
<div class="section-header" id="constantsHeader">
CONSTANTS & SCALING
<button id="toggleConstantsBtn" class="btn-icon"><i class="fa-solid fa-chevron-left"></i></button>
</div>
<div id="constantsContent" class="hidden-content">
<div id="scalingFactorsGrid" style="display: grid; grid-template-columns: 1fr 1fr; gap: 5px; margin-bottom: 10px; padding-bottom: 8px; border-bottom: 1px solid var(--panel-border); font-size: 10px;"></div>
<div style="padding: 5px 0;">
<div class="mini-input-group" style="margin-bottom: 8px;">
<label style="display:flex; justify-content:space-between; width:100%;"><span>\( c \) (Speed of light in a vacuum)</span></label>
<div style="display:flex; align-items:center; gap:5px;">
<input type="range" id="const-c-slider" class="slider" step="any" style="flex:1;">
<input type="text" id="const-c-text" class="text-inp" style="width:70px; font-family:monospace; font-size:10px; text-align:right;">
</div>
</div>
<div class="mini-input-group" style="margin-bottom: 8px;">
<label style="display:flex; justify-content:space-between; width:100%;"><span>\( G \) (Gravitational constant)</span></label>
<div style="display:flex; align-items:center; gap:5px;">
<input type="range" id="const-G-slider" class="slider" step="any" style="flex:1;">
<input type="text" id="const-G-text" class="text-inp" style="width:70px; font-family:monospace; font-size:10px; text-align:right;">
</div>
</div>
<div class="mini-input-group" style="margin-bottom: 8px;">
<label style="display:flex; justify-content:space-between; width:100%;"><span>\( h \) (Planck constant)</span></label>
<div style="display:flex; align-items:center; gap:5px;">
<input type="range" id="const-h-slider" class="slider" step="any" style="flex:1;">
<input type="text" id="const-h-text" class="text-inp" style="width:70px; font-family:monospace; font-size:10px; text-align:right;">
</div>
</div>
<div class="mini-input-group" style="margin-bottom: 8px;">
<label style="display:flex; justify-content:space-between; width:100%;"><span>\( K_e \) (Coulomb Constant)</span></label>
<div style="display:flex; align-items:center; gap:5px;">
<input type="range" id="const-Ke-slider" class="slider" step="any" style="flex:1;">
<input type="text" id="const-Ke-text" class="text-inp" style="width:70px; font-family:monospace; font-size:10px; text-align:right;">
</div>
</div>
<div class="mini-input-group" style="margin-bottom: 8px;">
<label style="display:flex; justify-content:space-between; width:100%;"><span>\( k_B \) (Boltzmann constant)</span></label>
<div style="display:flex; align-items:center; gap:5px;">
<input type="range" id="const-kB-slider" class="slider" step="any" style="flex:1;">
<input type="text" id="const-kB-text" class="text-inp" style="width:70px; font-family:monospace; font-size:10px; text-align:right;">
</div>
</div>
<div class="mini-input-group" style="margin-bottom: 8px;">
<label style="display:flex; justify-content:space-between; width:100%;"><span>\( N_A \) (Avogadro Constant)</span></label>
<div style="display:flex; align-items:center; gap:5px;">
<input type="range" id="const-NA-slider" class="slider" step="any" style="flex:1;">
<input type="text" id="const-NA-text" class="text-inp" style="width:70px; font-family:monospace; font-size:10px; text-align:right;">
</div>
</div>
<div class="mini-input-group" style="margin-bottom: 8px;">
<label style="display:flex; justify-content:space-between; width:100%;"><span>\( K_{cd} \) (Luminous efficacy)</span></label>
<div style="display:flex; align-items:center; gap:5px;">
<input type="range" id="const-Kcd-slider" class="slider" step="any" style="flex:1;">
<input type="text" id="const-Kcd-text" class="text-inp" style="width:70px; font-family:monospace; font-size:10px; text-align:right;">
</div>
</div>
</div>
</div>
</div>
</div>
<div id="tab-view" class="tab-pane">
<div class="control-section" id="displaySection">
<div class="section-header">Display parameters</div>
<div id="displayContent">
<label class="toggle-row">
<span>Tracking Barycentre <i class="fa-solid fa-circle-question help-icon" data-tooltip="Camera automatically tracks the system's center of mass."></i></span>
<input type="checkbox" id="camTrackingBox">
<div class="toggle-switch"></div>
</label>
<label class="toggle-row" style="margin-top:4px;">
<span>Auto Zoom <i class="fa-solid fa-circle-question help-icon" data-tooltip="Camera zooms automatically to keep all bodies visible."></i></span>
<input type="checkbox" id="camAutoZoomBox">
<div class="toggle-switch"></div>
</label>
<label class="toggle-row" style="margin-top:4px;">
<span>Physical Colors <i class="fa-solid fa-circle-question help-icon" data-tooltip="Color bodies based on Temperature (Hue), Velocity (Brightness), and Acceleration (Stress)."></i></span>
<input type="checkbox" id="physicalColorBox">
<div class="toggle-switch"></div>
</label>
<label class="toggle-row" style="margin-top:4px;">
<span>Show Trails <i class="fa-solid fa-circle-question help-icon" data-tooltip="Displays the recent trajectory of each body."></i></span>
<input type="checkbox" id="showTrailsBox" checked>
<div class="toggle-switch"></div>
</label>
<label class="toggle-row" style="margin-top:4px;">
<span>Show Gravity Field <i class="fa-solid fa-circle-question help-icon" data-tooltip="Visualizes the gravitational field generated by bodies."></i></span>
<input type="checkbox" id="showGravFieldBox">
<div class="toggle-switch"></div>
</label>
<label class="toggle-row" style="margin-top:4px;">
<span>Show Electric Field <i class="fa-solid fa-circle-question help-icon" data-tooltip="Visualizes the electric field generated by charged bodies."></i></span>
<input type="checkbox" id="showElecFieldBox">
<div class="toggle-switch"></div>
</label>
<label class="toggle-row" style="margin-top:4px;">
<span>Show Magnetic Field <i class="fa-solid fa-circle-question help-icon" data-tooltip="Visualizes the magnetic field generated by bodies."></i></span>
<input type="checkbox" id="showMagFieldBox">
<div class="toggle-switch"></div>
</label>
<label class="toggle-row" style="margin-top:4px;">
<span>Show Formula Field <i class="fa-solid fa-circle-question help-icon" data-tooltip="Visualizes force fields defined by custom formulas."></i></span>
<input type="checkbox" id="showFormulaFieldBox">
<div class="toggle-switch"></div>
</label>
<div class="divider-sub"></div>
<div class="section-header-2 toggle-header" id="advancedDisplayHeader">
<span>Advanced Display parameters</span>
<button id="toggleAdvancedDisplayBtn" class="btn-icon"><i class="fa-solid fa-chevron-left"></i></button>
</div>
<div id="displayAdevancedContent" class="hidden-content">
<div class="control-row" style="margin-top: 4px;">
<label>Trail Length <i class="fa-solid fa-circle-question help-icon" data-tooltip="Number of points in each body's trail."></i></label>
<span id="trailLenVal" class="value-display"></span>
</div>
<input type="range" id="trailLenSlider" min="10" max="500" step="10">
<div class="control-row" style="margin-top: 4px;">
<label>Trail Skip (Precision) <i class="fa-solid fa-circle-question help-icon" data-tooltip="Trail point recording frequency. Lower values are more precise."></i></label>
<span id="trailPrecVal" class="value-display"></span>
</div>
<input type="range" id="trailPrecSlider" min="1" max="10" step="1">
<div class="control-row" style="margin-top: 4px;">
<label>Prediction Length <i class="fa-solid fa-circle-question help-icon" data-tooltip="Length of the predicted future trajectory for the selected body."></i></label>
<span id="predictionLenVal" class="value-display"></span>
</div>
<input type="range" id="predictionLenSlider" min="0" max="70000" step="100">
<div class="control-row" style="margin-top: 4px;">
<label>Field Grid Precision <i class="fa-solid fa-circle-question help-icon" data-tooltip="Sets the density of the field visualization grid."></i></label>
<span id="fieldPrecVal" class="value-display"></span>
</div>
<input type="range" id="fieldPrecSlider" min="10" max="100" step="1">
<div class="control-row hidden-content" style="margin-top: 4px;">
<label>Field Intensity Scale <i class="fa-solid fa-circle-question help-icon" data-tooltip="Adjusts field vector length sensitivity to field intensity."></i></label>
<span id="fieldScaleVal" class="value-display"></span>
</div>
<input type="range" id="fieldScaleSlider" class="hidden-content" min="1" max="300" step="1">
<div class="control-row" style="margin-top: 4px;">
<label>Grid Precision <i class="fa-solid fa-circle-question help-icon" data-tooltip="Detail level of the spacetime grid."></i></label>
<span id="gridPrecVal" class="value-display"></span>
</div>
<input type="range" id="gridPrecSlider" min="1" max="100" step="1">
<div class="control-row" style="margin-top: 4px;">
<label>Spacetime Distortion <i class="fa-solid fa-circle-question help-icon" data-tooltip="Intensity of spacetime grid distortion by massive bodies."></i></label>
<span id="gridDistVal" class="value-display"></span>
</div>
<input type="range" id="gridDistSlider" min="0" max="5" step="0.01">
<div class="control-row" style="margin-top: 4px;">
<label>Min Def. Dist <i class="fa-solid fa-circle-question help-icon" data-tooltip="Minimum distance for distortion calculation, avoids singularities."></i></label>
<span id="gridMinDistVal" class="value-display"></span>
</div>
<input type="range" id="gridMinDistSlider" min="0.1" max="50" step="0.1">
</div>
</div>
</div>
</div>
<div id="tab-bodies" class="tab-pane">
<div class="control-section" id="injectionSection">
<div class="section-header toggle-header" id="injectionHeader">
<span>Injection parameters</span>
<button id="toggleInjectionBtn" class="btn-icon"><i class="fa-solid fa-chevron-down"></i></button>
</div>
<div id="injectionContent">
<div class="grid-2-col">
<div class="input-wrapper" style="grid-column: span 3;">
<label>Preset Object <i class="fa-solid fa-circle-question help-icon" data-tooltip="Selects a predefined object type with realistic physical parameters."></i></label>
<select id="presetSelect">
<option value="">Custom</option>
</select>
</div>
<div class="input-wrapper"><label>Mass <i class="fa-solid fa-circle-question help-icon" data-tooltip="Body mass. Drag label to adjust (Shift: precise, Ctrl: fast). Set to -1 for a fixed body."></i></label><input type="text" id="newMass"></div>
<div class="input-wrapper"><label>Radius <i class="fa-solid fa-circle-question help-icon" data-tooltip="Body radius for collisions."></i></label><input type="text" id="newRadius"></div>
<div class="input-wrapper"><label>Position X <i class="fa-solid fa-circle-question help-icon" data-tooltip="Initial X coordinate."></i></label><input type="text" id="newX"></div>
<div class="input-wrapper"><label>Position Y <i class="fa-solid fa-circle-question help-icon" data-tooltip="Initial Y coordinate."></i></label><input type="text" id="newY"></div>
<div class="input-wrapper"><label>Velocity X <i class="fa-solid fa-circle-question help-icon" data-tooltip="Initial velocity on X axis."></i></label><input type="text" id="newVX"></div>
<div class="input-wrapper"><label>Velocity Y <i class="fa-solid fa-circle-question help-icon" data-tooltip="Initial velocity on Y axis."></i></label><input type="text" id="newVY"></div>
<div class="input-wrapper"><label>Acceleration X <i class="fa-solid fa-circle-question help-icon" data-tooltip="Constant acceleration applied on X axis."></i></label><input type="text" id="newAX"></div>
<div class="input-wrapper"><label>Acceleration Y <i class="fa-solid fa-circle-question help-icon" data-tooltip="Constant acceleration applied on Y axis."></i></label><input type="text" id="newAY"></div>
<div class="input-wrapper"><label>Charge <i class="fa-solid fa-circle-question help-icon" data-tooltip="Body electric charge."></i></label><input type="text" id="newCharge"></div>
<div class="input-wrapper"><label>Mag. Moment <i class="fa-solid fa-circle-question help-icon" data-tooltip="Body magnetic moment."></i></label><input type="text" id="newMagMoment"></div>
<div class="input-wrapper"><label>Rot. Speed <i class="fa-solid fa-circle-question help-icon" data-tooltip="Body rotation speed."></i></label><input type="text" id="newRotationSpeed"></div>
<div class="input-wrapper"><label>Friction Coeff. <i class="fa-solid fa-circle-question help-icon" data-tooltip="Friction coefficient during collisions."></i></label><input type="text" id="newFriction" min="0" max="2" step="0.01"></div>
<div class="input-wrapper"><label>Integrity <i class="fa-solid fa-circle-question help-icon" data-tooltip="Structural integrity. Threshold for fragmentation from collisions or tidal forces."></i></label><input type="text" id="newIntegrity"></div>
<div class="input-wrapper"><label>Lifetime (ticks) <i class="fa-solid fa-circle-question help-icon" data-tooltip="Body lifetime in simulation 'ticks'. -1 for infinite."></i></label><input type="text" id="newLifetime" min="-1" step="1"></div>
</div>
<div class="divider-sub" style="margin-top: 8px;"></div>
<div class="section-header-2" style="margin-bottom: 5px;">Thermodynamics</div>
<div class="grid-2-col">
<div class="input-wrapper"><label>Temperature <i class="fa-solid fa-circle-question help-icon" data-tooltip="Initial body temperature in Kelvin."></i></label><input type="text" id="newTemperature"></div>
<div class="input-wrapper"><label>Heat Cap. <i class="fa-solid fa-circle-question help-icon" data-tooltip="Specific Heat Capacity (J/kg·K). Thermal inertia."></i></label><input type="text" id="newSpecificHeat"></div>
<div class="input-wrapper"><label>Absorp. Fact. <i class="fa-solid fa-circle-question help-icon" data-tooltip="Absorption Factor (0-1). Fraction of collision energy converted to heat."></i></label><input type="text" id="newAbsorptionFactor"></div>
<div class="input-wrapper"><label>Crit. Temp <i class="fa-solid fa-circle-question help-icon" data-tooltip="Critical Temperature (K) where material properties change."></i></label><input type="text" id="newCriticalTemp"></div>
<div class="input-wrapper"><label>Trans. Fact. <i class="fa-solid fa-circle-question help-icon" data-tooltip="Transition Factor. Defines the softness of the material property change around critical temperature."></i></label><input type="text" id="newTransitionFactor"></div>
<div class="input-wrapper"><label>Restit. (Cold) <i class="fa-solid fa-circle-question help-icon" data-tooltip="Base restitution coefficient (solid state)."></i></label><input type="text" id="newE_base"></div>
<div class="input-wrapper"><label>Restit. (Hot) <i class="fa-solid fa-circle-question help-icon" data-tooltip="Minimum restitution coefficient (liquid state)."></i></label><input type="text" id="newE_min"></div>
<div class="input-wrapper"><label>Young's M. (Cold) <i class="fa-solid fa-circle-question help-icon" data-tooltip="Base Young's Modulus (solid state)."></i></label><input type="text" id="newY_base"></div>
<div class="input-wrapper"><label>Young's M. (Hot) <i class="fa-solid fa-circle-question help-icon" data-tooltip="Minimum Young's Modulus (liquid state)."></i></label><input type="text" id="newY_min"></div>
</div>
<div class="divider-sub"></div>
<div class="input-wrapper" style="margin-top:5px;">
<button id="randomizeBtn" class="btn secondary"><i class="fa-solid fa-dice"></i> Random</button>
<button id="addBodyBtn" class="btn action"><i class="fa-solid fa-plus"></i> Inject</button>
</div>
</div>
</div>
<div class="divider"></div>
<div class="control-section">
<div class="section-header toggle-header" id="bodiesHeader">
<span>Active bodies <span id="bodyCount">0</span></span>
<button id="toggleBodiesBtn" class="btn-icon"><i class="fa-solid fa-chevron-left"></i></button>
</div>
<div id="bodySortContainer" class="hidden-content" style="margin-bottom: 8px; display: flex; gap: 5px;"></div>
<div id="bodiesListContainer" class="hidden-content">
</div>
</div>
</div>
<div id="tab-tools" class="tab-pane">
<div class="control-section">
<div class="section-header">Global Actions</div>
<div class="grid-2-col">
<button id="zeroVelBtn" class="btn secondary" title="Set all velocities to zero">
<i class="fa-solid fa-stop"></i> Stop
</button>
<button id="reverseVelBtn" class="btn secondary" title="Reverse time flow">
<i class="fa-solid fa-backward"></i> Reverse
</button>
<button id="killRotBtn" class="btn secondary" title="Reset angular velocity to zero">
<i class="fa-solid fa-arrows-spin"></i> No Spin
</button>
<button id="snapBtn" class="btn secondary" title="Align bodies to grid (50px)">
<i class="fa-solid fa-border-all"></i> Snap
</button>
<button id="scatterBtn" class="btn secondary" title="Randomize positions in view">
<i class="fa-solid fa-shuffle"></i> Scatter
</button>
<button id="equalMassBtn" class="btn secondary" title="Set all masses to average">
<i class="fa-solid fa-scale-balanced"></i> Equalize
</button>
<button id="cullBtn" class="btn danger" style="grid-column: span 3;" title="Remove all bodies outside the current camera view">
<i class="fa-solid fa-trash-can"></i> Cull Outside View
</button>
</div>
</div>
<div class="divider"></div>
<div class="control-section">
<div class="section-header">Field Definition</div>
<button id="addFieldBtn" class="btn secondary"><i class="fa-solid fa-plus"></i> Add Formula Field</button>
<div id="fieldDefCollapsible" style="display: none;">
<div class="divider-sub"></div>
<div class="section-header toggle-header" id="fieldDefListHeader">
<span>Defined Fields <span id="fieldListCount">0</span></span>
<button id="toggleFieldDefListBtn" class="btn-icon"><i class="fa-solid fa-chevron-down"></i></button>
</div>
<div id="fieldsListContainer" style="display: flex; flex-direction: column; gap: 8px;">
</div>
</div>
</div>
<div class="divider-sub"></div>
<div class="control-section">
<div class="section-header">Elastic Bonds</div>
<button id="toggleBondToolBtn" class="btn secondary">
<i class="fa-solid fa-link"></i> Link Bodies (Off)
</button>
<div id="bondsCollapsible" style="display: none;">
<div class="divider-sub"></div>
<div class="section-header toggle-header" id="bondsListHeader">
<span>Defined Bonds <span id="bondListCount">0</span></span>
<button id="toggleBondsListBtn" class="btn-icon"><i class="fa-solid fa-chevron-down"></i></button>
</div>
<div id="bondsListContainer" style="display: flex; flex-direction: column; gap: 8px;">
</div>
</div>
</div>
<div class="divider-sub"></div>
<div class="control-section">
<div class="section-header">Solid Barriers</div>
<button id="toggleBarrierToolBtn" class="btn secondary">
<i class="fa-solid fa-road"></i> Draw Barrier (Off)
</button>
<div id="barriersCollapsible" style="display: none;">
<div class="divider-sub"></div>
<div class="section-header toggle-header" id="barriersListHeader">
<span>Defined Barriers <span id="barrierListCount">0</span></span>
<button id="toggleBarriersListBtn" class="btn-icon"><i class="fa-solid fa-chevron-down"></i></button>
</div>
<div id="barriersListContainer" style="display: flex; flex-direction: column; gap: 8px;">
</div>
</div>
</div>
<div class="divider-sub"></div>
<div class="control-section">
<div class="section-header">Periodic Zones</div>
<div class="tool-group" style="display: flex; gap: 5px; align-items: center;">
<button id="toggleZoneDrawBtn" class="btn secondary">
<i class="fa-solid fa-pen-ruler"></i> Draw Zone (Off)
</button>
<div id="periodicZoneShapeSelector" class="btn-group" style="display: none;">
<button class="btn secondary active" data-shape="rectangle" title="Rectangle"><i class="fa-solid fa-square"></i></button>
<button class="btn secondary" data-shape="circle" title="Circle"><i class="fa-solid fa-circle"></i></button>
</div>
</div>
<div id="periodicZonesCollapsible" style="display: none;">
<div class="divider-sub"></div>
<div class="section-header-2 toggle-header" id="periodicZonesListHeader">
<span>Defined Zones <span id="periodicZoneListCount">0</span></span>
<button id="togglePeriodicZonesListBtn" class="btn-icon"><i class="fa-solid fa-chevron-down"></i></button>
</div>
<div id="zonesListContainer" style="display: flex; flex-direction: column; gap: 8px;">
</div>
</div>
</div>
<div class="divider-sub"></div>
<div class="control-section">
<div class="section-header">Viscosity Zones</div>
<div class="tool-group" style="display: flex; gap: 5px; align-items: center;">
<button id="toggleViscosityZoneBtn" class="btn secondary">
<i class="fa-solid fa-water"></i> Draw Viscosity (Off)
</button>
<div id="viscosityZoneShapeSelector" class="btn-group" style="display: none;">
<button class="btn secondary active" data-shape="rectangle" title="Rectangle"><i class="fa-solid fa-square"></i></button>
<button class="btn secondary" data-shape="circle" title="Circle"><i class="fa-solid fa-circle"></i></button>
</div>
</div>
<div id="viscosityZonesCollapsible" style="display: none;">
<div class="divider-sub"></div>
<div class="section-header-2 toggle-header" id="viscosityZonesListHeader">
<span>Defined Zones <span id="viscosityZoneListCount">0</span></span>
<button id="toggleViscosityZonesListBtn" class="btn-icon"><i class="fa-solid fa-chevron-down"></i></button>
</div>
<div id="viscosityZonesListContainer" style="display: flex; flex-direction: column; gap: 8px;">
</div>
</div>
</div>
<div class="divider-sub"></div>
<div class="control-section">
<div class="section-header">Field Zones</div>
<div class="tool-group" style="display: flex; gap: 5px; align-items: center;">
<button id="toggleFieldZoneToolBtn" class="btn secondary">
<i class="fa-solid fa-arrow-down"></i> Draw Field (Off)
</button>
<div id="fieldZoneShapeSelector" class="btn-group" style="display: none;">
<button class="btn secondary active" data-shape="rectangle" title="Rectangle"><i class="fa-solid fa-square"></i></button>
<button class="btn secondary" data-shape="circle" title="Circle"><i class="fa-solid fa-circle"></i></button>
</div>
</div>
<div id="fieldZonesCollapsible" style="display: none;">
<div class="divider-sub"></div>
<div class="section-header-2 toggle-header" id="fieldZonesListHeader">
<span>Defined Zones <span id="fieldZoneListCount">0</span></span>
<button id="toggleFieldZonesListBtn" class="btn-icon"><i class="fa-solid fa-chevron-down"></i></button>
</div>
<div id="fieldZonesListContainer" style="display: flex; flex-direction: column; gap: 8px;">
</div>
</div>
</div>
<div class="divider-sub"></div>
<div class="control-section">
<div class="section-header">Thermal Zones</div>
<div class="tool-group" style="display: flex; gap: 5px; align-items: center;">
<button id="toggleThermalZoneBtn" class="btn secondary">
<i class="fa-solid fa-temperature-high"></i> Draw Thermal (Off)
</button>
<div id="thermalZoneShapeSelector" class="btn-group" style="display: none;">
<button class="btn secondary active" data-shape="rectangle" title="Rectangle"><i class="fa-solid fa-square"></i></button>
<button class="btn secondary" data-shape="circle" title="Circle"><i class="fa-solid fa-circle"></i></button>
</div>
</div>
<div id="thermalZonesCollapsible" style="display: none;">
<div class="divider-sub"></div>
<div class="section-header-2 toggle-header" id="thermalZonesListHeader">
<span>Defined Zones <span id="thermalZoneListCount">0</span></span>
<button id="toggleThermalZonesListBtn" class="btn-icon"><i class="fa-solid fa-chevron-down"></i></button>
</div>
<div id="thermalZonesListContainer" style="display: flex; flex-direction: column; gap: 8px;">
</div>
</div>
</div>
<div class="divider-sub"></div>
<div class="control-section">
<div class="section-header">Annihilation Zones</div>
<div class="tool-group" style="display: flex; gap: 5px; align-items: center;">
<button id="toggleAnnihilationZoneBtn" class="btn secondary">
<i class="fa-solid fa-skull-crossbones"></i> Draw Annihilation (Off)
</button>
<div id="annihilationZoneShapeSelector" class="btn-group" style="display: none;">
<button class="btn secondary active" data-shape="rectangle" title="Rectangle"><i class="fa-solid fa-square"></i></button>
<button class="btn secondary" data-shape="circle" title="Circle"><i class="fa-solid fa-circle"></i></button>
</div>
</div>
<div id="annihilationZonesCollapsible" style="display: none;">
<div class="divider-sub"></div>
<div class="section-header-2 toggle-header" id="annihilationZonesListHeader">
<span>Defined Zones <span id="annihilationZoneListCount">0</span></span>
<button id="toggleAnnihilationZonesListBtn" class="btn-icon"><i class="fa-solid fa-chevron-down"></i></button>
</div>
<div id="annihilationZonesListContainer" style="display: flex; flex-direction: column; gap: 8px;">
</div>
</div>
</div>
<div class="divider-sub"></div>
<div class="control-section">
<div class="section-header">Chaos Zones</div>
<div class="tool-group" style="display: flex; gap: 5px; align-items: center;">
<button id="toggleChaosZoneBtn" class="btn secondary">
<i class="fa-solid fa-hurricane"></i> Draw Chaos (Off)
</button>
<div id="chaosZoneShapeSelector" class="btn-group" style="display: none;">
<button class="btn secondary active" data-shape="rectangle" title="Rectangle"><i class="fa-solid fa-square"></i></button>
<button class="btn secondary" data-shape="circle" title="Circle"><i class="fa-solid fa-circle"></i></button>
</div>
</div>
<div id="chaosZonesCollapsible" style="display: none;">
<div class="divider-sub"></div>
<div class="section-header-2 toggle-header" id="chaosZonesListHeader">
<span>Defined Zones <span id="chaosZoneListCount">0</span></span>
<button id="toggleChaosZonesListBtn" class="btn-icon"><i class="fa-solid fa-chevron-down"></i></button>
</div>
<div id="chaosZonesListContainer" style="display: flex; flex-direction: column; gap: 8px;">
</div>
</div>
</div>
<div class="divider-sub"></div>
<div class="control-section">
<div class="section-header">Vortex Zones</div>
<div class="tool-group" style="display: flex; gap: 5px; align-items: center;">
<button id="toggleVortexZoneBtn" class="btn secondary">
<i class="fa-solid fa-fan"></i> Draw Vortex (Off)
</button>
</div>
<div id="vortexZonesCollapsible" style="display: none;">
<div class="divider-sub"></div>
<div class="section-header-2 toggle-header" id="vortexZonesListHeader">
<span>Defined Zones <span id="vortexZoneListCount">0</span></span>
<button id="toggleVortexZonesListBtn" class="btn-icon"><i class="fa-solid fa-chevron-down"></i></button>
</div>
<div id="vortexZonesListContainer" style="display: flex; flex-direction: column; gap: 8px;">
</div>
</div>
</div>
<div class="divider-sub"></div>
<div class="control-section">
<div class="section-header">Null Zones</div>
<div class="tool-group" style="display: flex; gap: 5px; align-items: center;">
<button id="toggleNullZoneBtn" class="btn secondary">
<i class="fa-solid fa-ban"></i> Draw Null Zone (Off)
</button>
<div id="nullZoneShapeSelector" class="btn-group" style="display: none;">
<button class="btn secondary active" data-shape="rectangle" title="Rectangle"><i class="fa-solid fa-square"></i></button>
<button class="btn secondary" data-shape="circle" title="Circle"><i class="fa-solid fa-circle"></i></button>
</div>
</div>
<div id="nullZonesCollapsible" style="display: none;">
<div class="divider-sub"></div>
<div class="section-header-2 toggle-header" id="nullZonesListHeader">
<span>Defined Zones <span id="nullZoneListCount">0</span></span>
<button id="toggleNullZonesListBtn" class="btn-icon"><i class="fa-solid fa-chevron-down"></i></button>
</div>
<div id="nullZonesListContainer" style="display: flex; flex-direction: column; gap: 8px;">
</div>
</div>
</div>
<div id="toolsContent" class="hidden-content">
</div>
</div>
<div id="tab-infos" class="tab-pane">
<div class="control-section info-section">
<div class="section-header info-section-header">
<span>Physics, Models, and Algorithms</span>
<button class="btn-icon"><i class="fa-solid fa-chevron-left"></i></button>
</div>
<div class="info-content hidden-content">
<p>This simulation models the dynamics of a system of particles (N-bodies) interacting under various physical forces. The motion of these bodies is calculated over time using numerical integration techniques, which predict their state at a future time based on their current state. Here is a detailed breakdown of the core components:</p>
<h4>Core Physical Interactions</h4>
<p>The simulation accounts for several fundamental forces that can be enabled or disabled by the user.</p>
<ul>
<li><strong>Gravitation:</strong> The primary long-range force is gravity, modeled using Newton's law of universal gravitation. The force \( \vec{F}_g \) exerted on a body of mass \( m_1 \) by a body of mass \( m_2 \) is an attractive force directed along the line connecting their centers:
<br>
$$ \vec{F}_g = -G \frac{m_1 m_2}{r^2} \hat{r}_{21} $$
<br>
where \( G \) is the gravitational constant, \( r \) is the distance between the bodies, and \( \hat{r}_{21} \) is the unit vector pointing from body 2 to body 1. In the simulation, forces are calculated pair-wise, and the resulting acceleration \( \vec{a} = \vec{F}/\text{m} \) is applied to each body.
<br>When fragmentation is enabled, the simulation also considers <strong>tidal forces</strong>. These arise from the gravitational gradient across a body, which can stretch it apart if the force exceeds its structural integrity. The approximate tidal force is \( F_{tidal} \propto \frac{2 G M m R}{r^3} \), where M is the mass of the larger body and R is the radius of the smaller body m.
</li>
<li><strong>Electromagnetism:</strong> The simulation includes electrostatic and simplified magnetic forces.
<ul>
<li><strong>Electrostatics:</strong> Modeled by Coulomb's Law, which describes the force between two point charges \( q_1 \) and \( q_2 \):
<br>
$$ \vec{F}_e = K_e \frac{q_1 q_2}{r^2} \hat{r}_{21} $$
<br>
Here, \( K_e \) is Coulomb's constant. The force is repulsive for like charges (\(q_1 q_2 \gt 0\)) and attractive for opposite charges (\(q_1 q_2 \lt 0\)).
</li>
<li><strong>Magnetism:</strong> A simplified model for interactions between magnetic dipoles is used, assuming the dipoles are aligned. The force is approximated as being proportional to the inverse fourth power of distance:
<br>
$$ F_m \propto \frac{K_m \mu_1 \mu_2}{r^4} $$
<br>
where \( \mu_1 \) and \( \mu_2 \) are the scalar magnetic moments of the bodies and \( K_m \) is the magnetic force constant derived from \( K_e \) and the speed of light \(c\). This model captures the characteristic rapid fall-off of dipole-dipole interactions without the complexity of calculating orientation-dependent torques.
</li>
</ul>
</li>
<li><strong>Collisions and Contact Mechanics:</strong> When enabled, bodies can collide. The model is based on rigid-body dynamics with impulse resolution.
<ul>
<li><strong>Detection:</strong> A collision is detected when the distance \( d \) between the centers of two circular bodies is less than the sum of their radii (\( d \lt r_1 + r_2 \)).</li>
<li><strong>Resolution:</strong> An instantaneous impulse \( \vec{J} \) is calculated and applied to the bodies to resolve the collision. An impulse represents the change in momentum (\( \vec{J} = \Delta\vec{p} \)). The normal component of the impulse separates the bodies and is determined by the <strong>coefficient of restitution \( e \)</strong>, which dictates the elasticity of the collision (\( v'_{rel, n} = -e \cdot v_{rel, n} \)). An \( e=1 \) is a perfectly elastic collision, while \( e=0 \) is perfectly inelastic. The tangential component models friction using a <strong>Coulomb friction model</strong>, which opposes sliding motion.</li>
</ul>
</li>
<li><strong>Thermodynamics:</strong> This model introduces thermal energy and temperature-dependent physics.
<ul>
<li><strong>Heat from Collisions:</strong> In inelastic collisions (\( e \lt 1 \)), some kinetic energy is converted into thermal energy, increasing the bodies' temperatures based on their specific heat capacity. The generated heat \( Q \) is:
<br>
$$ Q = \Delta E_{kinetic} = \frac{1}{2} m_{reduced} v_{rel, n}^2 (1 - e^2) $$
where \( m_{reduced} = (m_1^{-1} + m_2^{-1})^{-1} \).
</li>
<li><strong>Radiative Cooling:</strong> Bodies cool down by emitting thermal radiation according to the Stefan-Boltzmann law. The net power radiated \( P \) depends on the body's temperature \( T \) and the ambient temperature \( T_{amb} \):
<br>
$$ P = \sigma \epsilon A (T^4 - T_{amb}^4) $$
<br>
where \( \sigma \) is the Stefan-Boltzmann constant, A is the surface area, and \( \epsilon \) is the emissivity (assumed to be 1).
</li>
<li><strong>Temperature-Dependent Properties:</strong> A body's material properties, such as its restitution \(e\) and Young's Modulus \(Y\), change with temperature to simulate phase transitions (e.g., solid to liquid). This is modeled using a logistic function:
<br>
$$ P_{current} = P_{min} + (P_{base} - P_{min}) \cdot \frac{1}{1 + e^{k(T - T_c)}} $$
where \( P_{base} \) is the property in the "cold" state, \( P_{min} \) in the "hot" state, \( T_c \) is the critical temperature, and \( k \) is the transition factor.
</li>
</ul>
</li>
</ul>
<h4>Algorithms and Numerical Methods</h4>
<ul>
<li><strong>Numerical Integration:</strong> To compute the trajectories, the simulation uses the <strong>Velocity Verlet</strong> integration algorithm. This is a numerical method for solving Newton's equations of motion. For each time step \( \Delta t \), it updates positions and velocities as follows:
<ol>
<li>Calculate velocity at the midpoint of the time step: \( \vec{v}(t + \frac{1}{2}\Delta t) = \vec{v}(t) + \frac{1}{2} \vec{a}(t) \Delta t \)</li>
<li>Update positions using this midpoint velocity: \( \vec{x}(t + \Delta t) = \vec{x}(t) + \vec{v}(t + \frac{1}{2}\Delta t) \Delta t \)</li>
<li>Calculate the new acceleration \( \vec{a}(t + \Delta t) \) based on the forces at the new position \( \vec{x}(t + \Delta t) \).</li>
<li>Update the final velocity: \( \vec{v}(t + \Delta t) = \vec{v}(t + \frac{1}{2}\Delta t) + \frac{1}{2} \vec{a}(t + \Delta t) \Delta t \)</li>
</ol>
This algorithm is chosen for its excellent long-term energy conservation, time-reversibility, and second-order accuracy, making it highly suitable for N-body simulations where stability is crucial.
</li>
<li><strong>Performance Optimization:</strong> Calculating the force between every pair of bodies (direct summation) has a computational complexity of O(N²), which becomes prohibitively slow for large N. To address this, the simulation employs the <strong>Barnes-Hut algorithm</strong>:
<ul>
<li>This algorithm approximates the force from a distant cluster of bodies as the force from a single "macro-particle" located at the cluster's center of mass.</li>
<li>It works by first recursively dividing the 2D space into a grid of cells using a <strong>quadtree</strong>. Each node in the tree represents a region of space.</li>
<li>To calculate the net force on a body, it traverses the tree. If a node is sufficiently far away, the force from all bodies within that node is approximated as a single interaction. The criterion for "sufficiently far" is given by the opening angle \( \theta \):
<br>
$$ \frac{s}{d} \lt \theta $$
<br>
where \( s \) is the width of the node's region, \( d \) is the distance to its center of mass, and \( \theta \) is a tunable accuracy parameter (typically ~0.5). If the condition is not met, the node is "opened," and its children are considered individually.</li>
<li>This hierarchical approach reduces the computational complexity to O(N log N), enabling simulations with thousands of bodies in real-time.</li>
</ul>
</li>
</ul>
</div>
</div>
<div class="control-section info-section">
<div class="section-header info-section-header">
<span>Features and Tools Guide</span>
<button class="btn-icon"><i class="fa-solid fa-chevron-left"></i></button>
</div>
<div class="info-content hidden-content">
<h4>Keyboard Shortcuts</h4>
<p>Optimize your workflow with these keyboard shortcuts:</p>
<ul>
<li><kbd>Spacebar</kbd>: Toggles the simulation between playing and paused states. This is the most frequently used shortcut for controlling the flow of time.</li>
<li><kbd>Ctrl</kbd> + <kbd>Z</kbd> or <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Y</kbd>: Undoes the last action performed, such as adding a body, creating a zone, or modifying a parameter. The simulation state is reverted to before the action.</li>
<li><kbd>Ctrl</kbd> + <kbd>Y</kbd> or <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Z</kbd>: Redoes an action that was previously undone. This is useful if you undo too many steps.</li>
<li><kbd>→</kbd> (Right Arrow): When the simulation is paused, this advances the simulation by a single time step (dt). It allows for frame-by-frame analysis of interactions.</li>
<li><kbd>←</kbd> (Left Arrow): When paused, this reverses time by one time step. Note that this is a true time reversal, not just an undo.</li>
<li><kbd>Shift</kbd> + <kbd>→</kbd> / <kbd>←</kbd>: Hold the Shift key while pressing the arrow keys to step forward or backward by 10 time steps at a time, for faster analysis.</li>
<li><kbd>F11</kbd>: Toggles your browser's fullscreen mode, providing a more immersive simulation experience without interface clutter.</li>
</ul>
<h4>Zone Types</h4>
<p>Zones are user-defined regions that apply unique physical effects to bodies within their boundaries. They are a powerful tool for creating complex and controlled simulation environments.</p>
<ul>
<li><strong>Periodic Zones:</strong> These zones create a "wrap-around" or toroidal space. When a body's center crosses one boundary of the zone, it is instantly teleported to the opposite side with its velocity intact. This is useful for simulating infinite, repeating environments or studying phenomena like crystal structures.</li>
<li><strong>Viscosity Zones:</strong> This simulates a region filled with a fluid-like medium (e.g., water or gas). It applies a drag force to any body inside it, which is proportional to the body's velocity but in the opposite direction. This causes bodies to slow down, mimicking the effect of friction in a fluid. Higher viscosity values result in a stronger drag.</li>
<li><strong>Field Zones:</strong> Applies a constant and uniform acceleration (a force per unit mass) to all bodies within its boundaries. This can be used to simulate effects like a uniform gravitational field near a planet's surface or a constant wind. The force is defined by its x and y components.</li>
<li><strong>Thermal Zones:</strong> These act as a thermostat for a region of space. Any body entering the zone will gradually heat up or cool down towards the zone's target temperature. The rate of heat transfer is determined by the zone's heat transfer coefficient. This is ideal for simulating environments like a hot star's corona or a cold nebula.</li>
<li><strong>Annihilation Zones:</strong> Acts as a "black hole" or destructive region. Any body that enters this zone is immediately removed from the simulation. You can optionally enable a "particle burst" effect, which creates a spray of small, short-lived particles upon a body's annihilation, simulating a destructive event.</li>
<li><strong>Chaos Zones:</strong> Introduces unpredictability by applying a randomized, fluctuating force field. The force vectors within the zone are generated using Perlin noise, creating a smoothly varying but chaotic motion. This can be used to simulate turbulence or other complex, non-linear environmental effects. Strength, frequency, and scale parameters allow for fine-tuning the chaotic behavior.</li>
<li><strong>Vortex Zones:</strong> Creates a swirling force field that pulls bodies towards a central point while also rotating them around it, similar to a whirlpool or a galaxy's spiral arm. The strength of the vortex typically decreases with distance from the center. This is perfect for simulating galactic dynamics or fluid vortices.</li>
<li><strong>Null Zones:</strong> This is a unique zone that allows you to "turn off" fundamental forces for any body inside it. You can selectively disable gravity, electricity, and/or magnetism. This is an advanced tool for creating force-free regions or studying how systems evolve when one of the fundamental interactions is suddenly removed.</li>
</ul>
<h4>Custom Formula Fields</h4>
<p>This is one of the most powerful and creative tools in the simulation. It allows you to define your own custom force fields using mathematical expressions. The simulation interprets these formulas to calculate the components of acceleration (<code>E_x</code> and <code>E_y</code>) that should be applied to a body at any point (<code>x</code>, <code>y</code>) in space. This force is proportional to the body's charge.</p>
<ul>
<li><strong>Available Variables:</strong> Your formulas can use the following built-in variables:
<ul>
<li><code>x</code>, <code>y</code>: The current coordinates of the point in space where the force is being calculated.</li>
<li><code>t</code>: The current simulation time in seconds. This allows for time-varying fields.</li>
<li><code>G</code>, <code>c</code>, <code>Ke</code>: The current values of the simulation's physical constants (Gravitational constant, Speed of light, Coulomb's constant).</li>
<li><code>PI</code>, <code>E</code>: The mathematical constants π and e.</li>
</ul>
</li>
<li><strong>Supported Functions & Syntax:</strong>
<ul>
<li>You can use all standard JavaScript Math functions, such as <code>sin()</code>, <code>cos()</code>, <code>tan()</code>, <code>sqrt()</code>, <code>log()</code>, <code>abs()</code>, <code>pow()</code>, etc.</li>
<li>Standard operators <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>, <code>%</code> are supported.</li>
<li>For exponentiation, you can use either <code>**</code> or <code>^</code> (e.g., <code>x^2</code> is the same as <code>x**2</code>).</li>
<li>The parser will automatically insert multiplication signs where they are implied, for example, <code>2x</code> will be interpreted as <code>2*x</code>, and <code>(x+1)(y-1)</code> will be interpreted as <code>(x+1)*(y-1)</code>.</li>
</ul>
</li>
<li><strong>Example: A Pulsating Quadrupole Field</strong>
<p>Let's create a more complex field that changes over time. A quadrupole field's strength falls off faster than a simple dipole. We'll make it pulsate using the time variable <code>t</code>.</p>
<p><code>E_x: sin(t) * (x*y) / (x*x + y*y)^2.5</code></p>
<p><code>E_y: sin(t) * (y*y - x*x/2) / (x*x + y*y)^2.5</code></p>
<p>This creates a complex, oscillating field that will push and pull charged particles in intricate patterns. Experimenting with these formulas is key to discovering new and interesting behaviors.</p>
</li>
</ul>
</div>
</div>
<div class="control-section info-section">
<div class="section-header info-section-header">
<span>Basic Controls</span>
<button class="btn-icon"><i class="fa-solid fa-chevron-left"></i></button>
</div>
<div class="info-content hidden-content">
<h4>Navigation</h4>
<ul>
<li><strong>Pan:</strong> Click and drag on an empty area of the canvas.</li>
<li><strong>Zoom:</strong> Use the mouse wheel to zoom in and out. The zoom is centered on your cursor's position. On touch devices, use a two-finger pinch gesture.</li>
</ul>
<h4>Interacting with Bodies</h4>
<ul>
<li><strong>Select a Body:</strong> Click on a body to select it. Its properties will be highlighted in the "Bodies" tab, and a velocity vector will appear.</li>
<li><strong>Move a Body:</strong> Click and drag a selected body to reposition it. The simulation will pause automatically.</li>
<li><strong>Adjust Velocity:</strong> Click and drag the white circle at the tip of the selected body's velocity vector to change its speed and direction.</li>
</ul>
<h4>Using Tools (in the "Tools" tab)</h4>
<ul>
<li><strong>Drawing Zones/Barriers:</strong> Click the tool button (e.g., "Draw Zone") to activate draw mode. Then, click and drag on the canvas to define the shape's boundaries. For circles, the start point is the center and you drag to define the radius.</li>
<li><strong>Creating Bonds:</strong> Activate the "Link Bodies" tool. Click and drag from one body to another to create an elastic bond between them.</li>
</ul>
</div>
</div>
<div class="control-section info-section">
<div class="section-header info-section-header">
<span>Simulation Units vs. SI Units</span>
<button class="btn-icon"><i class="fa-solid fa-chevron-left"></i></button>
</div>
<div class="info-content hidden-content">
<p>To ensure numerical stability and computational efficiency, this simulation does not directly use standard real-world units (like meters, kilograms, seconds). Instead, it operates within an internal, dimensionless system. The values for physical constants, such as the gravitational constant \( G \) or the speed of light \( c \), are scaled to values that are more manageable for the computer. This section details the relationship between these internal simulation units and the corresponding SI (International System of Units) values.</p>
<h4>The Concept of Simulation Units</h4>
<p>In the simulation's universe, the fundamental physical constants can be adjusted in the "Constants & Scaling" section of the "Simulation" tab. Changing these values fundamentally alters the physics of the simulation. For example:</p>
<ul>
<li>A larger simulation \( G \) value results in stronger gravitational forces relative to other forces.</li>
<li>A smaller simulation \( c \) value makes relativistic effects more pronounced at lower speeds.</li>
</ul>
<p>These adjustable constants (\(G_{sim}, c_{sim}, K_{e,sim}\), etc.) define the behavior of interactions within the simulation's context.</p>
<h4>Scaling Factors: The Bridge to Reality</h4>
<p>The "Constants & Scaling" section also displays a set of read-only values called <strong>Scaling Factors</strong> (\(T_0, L_0, M_0\), etc.). These factors are the conversion rates that bridge the gap between the simulation's internal units and the standard SI units. They are dynamically calculated based on the current values of the simulation's fundamental constants.</p>
<p>The core relationship is defined as follows:</p>
<p style="text-align: center; font-style: italic;">\( \text{Value}_{SI} = \text{Value}_{sim} \times \text{Scaling Factor} \)</p>
<p>For example:</p>
<ul>
<li>If the scaling factor for length (\(L_0\)) is \(1.496 \times 10^{11}\), it means that 1 unit of length in the simulation corresponds to \(1.496 \times 10^{11}\) meters in the real world (this is approximately one Astronomical Unit, the distance from the Earth to the Sun).</li>
<li>Similarly, if the scaling factor for mass (\(M_0\)) is \(5.972 \times 10^{24}\), then a body with a mass of 1 in the simulation represents an object with a mass of \(5.972 \times 10^{24}\) kilograms (the mass of the Earth).</li>
</ul>
<p>These scaling factors allow you to interpret the simulation in a real-world context or to set up scenarios that mimic known physical systems. By adjusting the base simulation constants, you can change these scaling factors to suit the scale of the system you wish to model, from planetary systems to subatomic particles.</p>
<h4>Derivation of Scaling Factors</h4>
<p>The scaling factors are not arbitrary. They are derived from the principle of dimensional analysis, ensuring that the laws of physics remain consistent between the simulation and the real world. The relationship for any physical constant \( K \) can be written as:</p>
<p> \( K_{SI} = K_{sim} \times \text{Factor}_K \)</p>
<p>Where \( \text{Factor}_K \) is the scaling factor for that constant's dimensions. For instance, the dimensions of the gravitational constant \( G \) are \( [L]^3 [M]^{-1} [T]^{-2} \). Therefore, its scaling factor is \( L_0^3 M_0^{-1} T_0^{-2} \). This leads to the equation:</p>
<p> \( G_{SI} = G_{sim} \times (L_0^3 M_0^{-1} T_0^{-2}) \)</p>
<p>By establishing similar relationships for other fundamental constants (like \( c \) and \( K_e \)), a system of equations is formed. Solving this system allows the simulation to determine the base scaling factors (\(L_0, M_0, T_0, Q_0\)...) from the user-defined simulation constants (\(G_{sim}, c_{sim}, K_{e,sim}\)...).</p>
</div>
</div>
<div class="control-section info-section">
<div class="section-header info-section-header">
<span>Performance Tips</span>
<button class="btn-icon"><i class="fa-solid fa-chevron-left"></i></button>
</div>
<div class="info-content hidden-content">
<p>N-body simulations can be computationally intensive. If you experience low frame rates (FPS), try the following:</p>
<ul>
<li><strong>Reduce Body Count:</strong> The number of bodies has the largest impact on performance. Use the "Cull Outside View" tool or manually delete bodies.</li>
<li><strong>Adjust Time Step (dt):</strong> Increasing the time step in the "Simulation" tab will make the simulation run faster, but can reduce accuracy and stability. Find a balance that works for your scenario.</li>
<li><strong>Disable Visual Effects:</strong> In the "View" tab, turn off computationally expensive effects like "Show Trails", "Show ... Field", and reduce "Spacetime Distortion".</li>
<li><strong>Use Barnes-Hut:</strong> For a large number of bodies, ensure the Barnes-Hut algorithm is enabled in the simulation settings for a significant performance boost.</li>
<li><strong>Disable Interactions:</strong> If you don't need certain forces (e.g., Magnetism, Thermodynamics), disabling them in the "Simulation" tab can save computational resources.</li>
</ul>
</div>
</div>
<div class="control-section info-section">
<div class="section-header info-section-header">
<span>About and Credits</span>
<button class="btn-icon"><i class="fa-solid fa-chevron-down"></i></button>
</div>
<div class="info-content">
<h4>About the Project</h4>
<p>This is an interactive N-Body physics simulation built with HTML, CSS, and JavaScript. It aims to provide an educational and entertaining tool for exploring orbital mechanics, electromagnetism, and other physical phenomena directly in the browser.</p>
<h4>Credits & Libraries</h4>
<ul>
<li><strong>Font Awesome:</strong> For the icons used throughout the user interface.</li>
<li><strong>KaTeX:</strong> For rendering of mathematical formulas.</li>
</ul>
<h4>License</h4>
<p>This project is open-source and licensed under the <a href="https://github.com/Wartets/N-Body-Simulation/blob/main/LICENSE" target="_blank" rel="noopener noreferrer">MIT License</a>.</p>
<h4>Author & Source Code</h4>
<p>Designed and developed by Wartets (Colin Bossu Réaubourg). You can explore more projects on my <a href="https://wartets.github.io/Wartets/" target="_blank" rel="noopener noreferrer">personal page</a> or check out the source code for this simulation on <a href="https://github.com/wartets/N-Body-Simulation" target="_blank" rel="noopener noreferrer">GitHub</a>.</p>
</div>
</div>
</div>
</div>
</div>
<script src="simulation.js"></script>
<script src="rendering.js"></script>
<script src="interface.js"></script>
<script src="presets.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
if (window.renderMathInElement) {
renderMathInElement(document.body, {
delimiters: [
{left: '$$', right: '$$', display: true},
{left: '\\(', right: '\\)', display: false}
]
});
}
});
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js');
}
</script>
</body>
</html>