33< head >
44 < meta charset ="UTF-8 ">
55 < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
6- < title > Pixel Swirl Version 1.006 </ title > < style >
6+ < title > Pixel Swirl Version 1.007 </ title > < style >
77 body {
88 font-family : Arial, sans-serif;
99 display : flex; /* Changed to flex to control main content better */
147147 button : hover {
148148 background-color : # 0056b3 ;
149149 }
150+
151+ /* Styling for the new Defaults button */
152+ button # defaultsButton {
153+ background-color : # dc3545 ; /* Red color */
154+ }
155+
156+ button # defaultsButton : hover {
157+ background-color : # c82333 ; /* Darker red on hover */
158+ }
159+
150160 input [type = "file" ] {
151161 display : none;
152162 }
200210 .app-info a : hover {
201211 text-decoration : underline;
202212 }
213+ .not-mobile {
214+ font-style : italic;
215+ font-size : 0.9em ;
216+ color : # aaa ;
217+ margin-top : 5px ;
218+ margin-bottom : 5px ;
219+ }
203220 </ style >
204221</ head >
205222< body >
282299
283300 < div class ="controls-row ">
284301 < div class ="control-group ">
285- < label for ="frequencySlider "> Frequency:</ label >
302+ < label for ="frequencySlider "> Frequency (seconds) :</ label >
286303 < div class ="slider-with-value ">
287304 < input type ="range " id ="frequencySlider " min ="0 " max ="10 " value ="0 " step ="1 ">
288305 < span id ="frequencyValue "> 0</ span >
304321 </ div >
305322 </ div >
306323
324+ < div class ="controls-row ">
325+ < button id ="defaultsButton "> Defaults</ button >
326+ </ div >
327+
328+
307329 < div class ="app-info ">
308330 < h2 > Pixel Swirl</ h2 >
309- < span id ="versionNumber "> Version 1.006</ span >
331+ < span id ="versionNumber "> Version 1.007</ span >
332+ < span class ="not-mobile "> (Not for Mobile Use)</ span >
310333 < hr > < br >
311334 Check out other cool stuff:< br >
312335 < br >
@@ -332,16 +355,16 @@ <h2>Pixel Swirl</h2>
332355 const gravityValueSpan = document . getElementById ( 'gravityValue' ) ;
333356 const snapDistanceSlider = document . getElementById ( 'snapDistanceSlider' ) ;
334357 const snapDistanceValueSpan = document . getElementById ( 'snapDistanceValue' ) ;
335- const repelSlider = document . getElementById ( 'repelSlider' ) ; // New Repel slider
336- const repelValueSpan = document . getElementById ( 'repelValue' ) ; // New Repel value span
337- const frequencySlider = document . getElementById ( 'frequencySlider' ) ; // New Frequency slider
338- const frequencyValueSpan = document . getElementById ( 'frequencyValue' ) ; // New Frequency value span
358+ const repelSlider = document . getElementById ( 'repelSlider' ) ;
359+ const repelValueSpan = document . getElementById ( 'repelValue' ) ;
360+ const frequencySlider = document . getElementById ( 'frequencySlider' ) ;
361+ const frequencyValueSpan = document . getElementById ( 'frequencyValue' ) ;
339362 const unlockPieceSizeCheckbox = document . getElementById ( 'unlockPieceSizeCheckbox' ) ;
340363 const unlockFreedomCheckbox = document . getElementById ( 'unlockFreedomCheckbox' ) ;
341364 const versionNumberSpan = document . getElementById ( 'versionNumber' ) ;
342365 const maxPiecesInput = document . getElementById ( 'maxPiecesInput' ) ;
366+ const defaultsButton = document . getElementById ( 'defaultsButton' ) ; // New defaults button
343367
344- // Added more granular small sizes
345368 const PUZZLE_PIECE_SIZES = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 16 , 32 , 64 , 128 , 256 , 512 ] ;
346369
347370 let TARGET_MAX_PIECES = 100000 ;
@@ -351,14 +374,14 @@ <h2>Pixel Swirl</h2>
351374 const DEFAULT_GRAVITY_MIN = 0.01 ;
352375 const DEFAULT_GRAVITY_MAX = 0.5 ;
353376 const DEFAULT_SNAP_MAX = 16 ;
354- const DEFAULT_REPEL_MAX = 1 ; // New default repel max
377+ const DEFAULT_REPEL_MAX = 1 ;
355378
356379 // Unlocked ranges for sliders
357380 const UNLOCKED_SPEED_MAX = 10 ;
358381 const UNLOCKED_GRAVITY_MIN = 0.001 ;
359382 const UNLOCKED_GRAVITY_MAX = 0.99 ;
360383 const UNLOCKED_SNAP_MAX = 256 ;
361- const UNLOCKED_REPEL_MAX = 5 ; // New unlocked repel max
384+ const UNLOCKED_REPEL_MAX = 5 ;
362385
363386
364387 let gl = null ;
@@ -373,8 +396,8 @@ <h2>Pixel Swirl</h2>
373396 let animationSpeed = 1 ;
374397 let gravityForce = 0.05 ;
375398 let snapDistance = 0 ;
376- let repelForce = 0 ; // New: Repel force
377- let repelFrequency = 0 ; // New: Repel frequency (0-10)
399+ let repelForce = 0 ;
400+ let repelFrequency = 0 ; // In seconds (0-10)
378401
379402 let puzzlePieces = [ ] ;
380403 let originalImageWidth = 0 ;
@@ -385,9 +408,11 @@ <h2>Pixel Swirl</h2>
385408 let allPositions = null ;
386409 let allTexCoords = null ;
387410
388- // Stores the user's preferred pixel size index, to be restored on restart
389411 let lastSetPuzzlePieceSizeIndex = PUZZLE_PIECE_SIZES . indexOf ( 64 ) ;
390412
413+ // For frequency-based repel
414+ let lastRepelTime = 0 ; // Timestamp of the last repel impulse
415+
391416
392417 // --- Shaders ---
393418 const vsSource = `
@@ -505,7 +530,7 @@ <h2>Pixel Swirl</h2>
505530 this . swirlSpeed = Math . random ( ) * 0.05 + 0.01 ;
506531 }
507532
508- update ( ) {
533+ update ( deltaTime ) { // Add deltaTime parameter for frame-independent updates
509534 if ( this . settled ) {
510535 return ;
511536 }
@@ -540,14 +565,13 @@ <h2>Pixel Swirl</h2>
540565
541566 let effectiveForce = gravityForce * animationSpeed ; // Default to attraction
542567
543- // Repel logic: if repelForce is active and a random check passes
544- if ( repelForce > 0 && repelFrequency > 0 ) {
545- if ( Math . random ( ) * 10 < repelFrequency ) { // Probability check (e.g., 5/10 chance if frequency=5)
546- effectiveForce = - repelForce ; // Negative force means repulsion
547- }
568+ // Repel logic: only apply if the global condition for repel is met this frame
569+ // (This piece does not do the frequency check itself, a global timer does)
570+ if ( repelForce > 0 && repelFrequency > 0 && repelTriggeredThisFrame ) {
571+ effectiveForce = - repelForce ; // Negative force means repulsion
548572 }
549573
550- this . vx += swirlDx * effectiveForce ; // Apply the calculated effective force
574+ this . vx += swirlDx * effectiveForce ;
551575 this . vy += swirlDy * effectiveForce ;
552576
553577 this . vx *= this . damping ;
@@ -625,12 +649,11 @@ <h2>Pixel Swirl</h2>
625649
626650 function updatePieceSizeSliderRange ( ) {
627651 if ( unlockPieceSizeCheckbox . checked ) {
628- pixelSizeSlider . min = 0 ; // Allow 1x1 pieces
652+ pixelSizeSlider . min = 0 ;
629653 } else {
630654 pixelSizeSlider . min = currentMinPieceSizeIndex ;
631655 }
632656
633- // Ensure slider value is not below the new minimum or above max
634657 let sliderVal = parseInt ( pixelSizeSlider . value ) ;
635658 const sliderMin = parseInt ( pixelSizeSlider . min ) ;
636659 const sliderMax = parseInt ( pixelSizeSlider . max ) ;
@@ -770,15 +793,38 @@ <h2>Pixel Swirl</h2>
770793 startAnimation ( ) ;
771794 }
772795
773- function drawScene ( ) {
796+ let previousTimeStamp = 0 ;
797+ let repelTriggeredThisFrame = false ; // Flag to indicate if repel is active for the current frame
798+
799+ function drawScene ( currentTimeStamp ) {
800+ if ( ! previousTimeStamp ) previousTimeStamp = currentTimeStamp ;
801+ const deltaTime = ( currentTimeStamp - previousTimeStamp ) / 1000 ; // Convert to seconds
802+ previousTimeStamp = currentTimeStamp ;
803+
774804 gl . clear ( gl . COLOR_BUFFER_BIT ) ;
775805
776806 let currentPositionIndex = 0 ;
777807 let allSettled = true ;
778808
809+ // Determine if a repel impulse should be triggered this frame
810+ repelTriggeredThisFrame = false ;
811+ if ( repelForce > 0 && repelFrequency > 0 ) {
812+ // Time since last repel in milliseconds
813+ const timeSinceLastRepel = currentTimeStamp - lastRepelTime ;
814+ // Required interval in milliseconds
815+ const requiredInterval = repelFrequency * 1000 ;
816+
817+ if ( timeSinceLastRepel >= requiredInterval ) {
818+ repelTriggeredThisFrame = true ;
819+ lastRepelTime = currentTimeStamp ; // Update last repel time
820+ console . log ( `Repel triggered at ${ currentTimeStamp . toFixed ( 0 ) } ms (interval: ${ requiredInterval } ms)` ) ;
821+ }
822+ }
823+
824+
779825 puzzlePieces . forEach ( piece => {
780826 if ( ! piece . settled ) {
781- piece . update ( ) ;
827+ piece . update ( deltaTime ) ; // Pass deltaTime to update if piece logic needs it (not currently for this version, but good practice)
782828 allSettled = false ;
783829 }
784830
@@ -838,6 +884,8 @@ <h2>Pixel Swirl</h2>
838884 p . vx = 0 ;
839885 p . vy = 0 ;
840886 } ) ;
887+ lastRepelTime = performance . now ( ) ; // Reset repel timer on animation start
888+ previousTimeStamp = 0 ; // Reset previous timestamp for accurate delta time
841889 drawScene ( ) ;
842890 }
843891 }
@@ -854,14 +902,12 @@ <h2>Pixel Swirl</h2>
854902 originalImageObject = img ;
855903 resizeCanvasToImage ( img ) ;
856904
857- // Store the current pixel size before potentially adjusting for new image/max pieces
858905 lastSetPuzzlePieceSizeIndex = parseInt ( pixelSizeSlider . value ) ;
859906
860907 calculateMinPieceSizeForImage ( ) ;
861908
862- // Set slider to the greater of user's last setting or the new minimum
863909 pixelSizeSlider . value = Math . max ( lastSetPuzzlePieceSizeIndex , currentMinPieceSizeIndex ) ;
864- pixelSizeSlider . dispatchEvent ( new Event ( 'input' ) ) ; // This will call createPuzzlePiecesFromImage()
910+ pixelSizeSlider . dispatchEvent ( new Event ( 'input' ) ) ;
865911 } ;
866912 img . src = e . target . result ;
867913 } ;
@@ -882,9 +928,8 @@ <h2>Pixel Swirl</h2>
882928
883929 calculateMinPieceSizeForImage ( ) ;
884930
885- // Set slider to the greater of user's last setting or the new minimum
886931 pixelSizeSlider . value = Math . max ( lastSetPuzzlePieceSizeIndex , currentMinPieceSizeIndex ) ;
887- pixelSizeSlider . dispatchEvent ( new Event ( 'input' ) ) ; // This will trigger createPuzzlePiecesFromImage()
932+ pixelSizeSlider . dispatchEvent ( new Event ( 'input' ) ) ;
888933 } else {
889934 alert ( "Please load an image first!" ) ;
890935 }
@@ -895,7 +940,6 @@ <h2>Pixel Swirl</h2>
895940 speedValueSpan . textContent = animationSpeed . toFixed ( 1 ) ;
896941 puzzlePieces . forEach ( p => {
897942 p . maxSpeed = 10 * animationSpeed ;
898- // No need to update p.attractionForce here, it's calculated in update()
899943 } ) ;
900944 if ( animationFrameId === null && originalImageObject ) {
901945 startAnimation ( ) ;
@@ -906,7 +950,7 @@ <h2>Pixel Swirl</h2>
906950 const sliderIndex = parseInt ( event . target . value ) ;
907951 currentPuzzlePieceSize = PUZZLE_PIECE_SIZES [ sliderIndex ] ;
908952 pixelSizeValueSpan . textContent = `${ currentPuzzlePieceSize } x${ currentPuzzlePieceSize } ` ;
909- lastSetPuzzlePieceSizeIndex = sliderIndex ; // Save the current slider index
953+ lastSetPuzzlePieceSizeIndex = sliderIndex ;
910954 if ( originalImageObject ) {
911955 createPuzzlePiecesFromImage ( ) ;
912956 }
@@ -934,7 +978,6 @@ <h2>Pixel Swirl</h2>
934978 gravitySlider . addEventListener ( 'input' , ( event ) => {
935979 gravityForce = parseFloat ( event . target . value ) ;
936980 gravityValueSpan . textContent = gravityForce . toFixed ( gravityForce < 0.01 ? 3 : 2 ) ;
937- // PuzzlePiece.update uses gravityForce directly, no need to loop here
938981 if ( animationFrameId === null && originalImageObject ) {
939982 startAnimation ( ) ;
940983 }
@@ -947,15 +990,20 @@ <h2>Pixel Swirl</h2>
947990
948991 repelSlider . addEventListener ( 'input' , ( event ) => {
949992 repelForce = parseFloat ( event . target . value ) ;
950- repelValueSpan . textContent = repelForce . toFixed ( 2 ) ; // Display 2 decimal places for repel
993+ repelValueSpan . textContent = repelForce . toFixed ( 2 ) ;
994+ // If repel is turned on from 0, or changed, and not animating, start it.
951995 if ( animationFrameId === null && originalImageObject ) {
952996 startAnimation ( ) ;
953997 }
954998 } ) ;
955999
9561000 frequencySlider . addEventListener ( 'input' , ( event ) => {
9571001 repelFrequency = parseInt ( event . target . value ) ;
958- frequencyValueSpan . textContent = repelFrequency . toString ( ) ; // Display as integer
1002+ frequencyValueSpan . textContent = repelFrequency . toString ( ) ;
1003+ // Reset lastRepelTime when frequency changes to ensure immediate effect or proper new timing
1004+ if ( repelFrequency > 0 ) {
1005+ lastRepelTime = performance . now ( ) ;
1006+ }
9591007 if ( animationFrameId === null && originalImageObject ) {
9601008 startAnimation ( ) ;
9611009 }
@@ -972,13 +1020,13 @@ <h2>Pixel Swirl</h2>
9721020 gravitySlider . min = UNLOCKED_GRAVITY_MIN ;
9731021 gravitySlider . max = UNLOCKED_GRAVITY_MAX ;
9741022 snapDistanceSlider . max = UNLOCKED_SNAP_MAX ;
975- repelSlider . max = UNLOCKED_REPEL_MAX ; // Unlocked repel max
1023+ repelSlider . max = UNLOCKED_REPEL_MAX ;
9761024 } else {
9771025 speedSlider . max = DEFAULT_SPEED_MAX ;
9781026 gravitySlider . min = DEFAULT_GRAVITY_MIN ;
9791027 gravitySlider . max = DEFAULT_GRAVITY_MAX ;
9801028 snapDistanceSlider . max = DEFAULT_SNAP_MAX ;
981- repelSlider . max = DEFAULT_REPEL_MAX ; // Default repel max
1029+ repelSlider . max = DEFAULT_REPEL_MAX ;
9821030 }
9831031 // Ensure values are within new bounds and update display
9841032 speedSlider . value = Math . min ( parseFloat ( speedSlider . value ) , parseFloat ( speedSlider . max ) ) ;
@@ -1009,9 +1057,37 @@ <h2>Pixel Swirl</h2>
10091057 console . log ( "Max # of Pieces changed to:" , maxPiecesInput . value , "Will apply on Restart." ) ;
10101058 } ) ;
10111059
1060+ defaultsButton . addEventListener ( 'click' , ( ) => {
1061+ // Reset all sliders to their default initial values
1062+ speedSlider . value = 1 ;
1063+ gravitySlider . value = 0.05 ;
1064+ snapDistanceSlider . value = 0 ;
1065+ repelSlider . value = 0 ;
1066+ frequencySlider . value = 0 ;
1067+ maxPiecesInput . value = 100000 ;
1068+ pixelSizeSlider . value = PUZZLE_PIECE_SIZES . indexOf ( 64 ) ; // Reset to 64x64
1069+ lastSetPuzzlePieceSizeIndex = PUZZLE_PIECE_SIZES . indexOf ( 64 ) ;
1070+
1071+ // Reset checkboxes
1072+ unlockFreedomCheckbox . checked = false ;
1073+ unlockPieceSizeCheckbox . checked = false ;
1074+
1075+ // Trigger input events to update UI and apply values
1076+ speedSlider . dispatchEvent ( new Event ( 'input' ) ) ;
1077+ gravitySlider . dispatchEvent ( new Event ( 'input' ) ) ;
1078+ snapDistanceSlider . dispatchEvent ( new Event ( 'input' ) ) ;
1079+ repelSlider . dispatchEvent ( new Event ( 'input' ) ) ;
1080+ frequencySlider . dispatchEvent ( new Event ( 'input' ) ) ;
1081+ maxPiecesInput . dispatchEvent ( new Event ( 'change' ) ) ; // Use 'change' for number inputs
1082+ pixelSizeSlider . dispatchEvent ( new Event ( 'input' ) ) ; // This also handles createPuzzlePiecesFromImage
1083+ unlockFreedomCheckbox . dispatchEvent ( new Event ( 'change' ) ) ; // This re-sets slider ranges
1084+ unlockPieceSizeCheckbox . dispatchEvent ( new Event ( 'change' ) ) ;
1085+
1086+ console . log ( "All settings reset to defaults." ) ;
1087+ } ) ;
10121088
10131089 // App Versioning
1014- const APP_VERSION = 1.006 ;
1090+ const APP_VERSION = 1.007 ;
10151091
10161092 // Initialize on load
10171093 window . onload = ( ) => {
@@ -1023,7 +1099,7 @@ <h2>Pixel Swirl</h2>
10231099 pixelSizeSlider . value = 0 ;
10241100 currentPuzzlePieceSize = PUZZLE_PIECE_SIZES [ 0 ] ;
10251101 }
1026- lastSetPuzzlePieceSizeIndex = parseInt ( pixelSizeSlider . value ) ; // Store initial value
1102+ lastSetPuzzlePieceSizeIndex = parseInt ( pixelSizeSlider . value ) ;
10271103
10281104 pixelSizeValueSpan . textContent = `${ currentPuzzlePieceSize } x${ currentPuzzlePieceSize } ` ;
10291105
@@ -1032,8 +1108,8 @@ <h2>Pixel Swirl</h2>
10321108 gravitySlider . min = DEFAULT_GRAVITY_MIN ;
10331109 gravitySlider . max = DEFAULT_GRAVITY_MAX ;
10341110 snapDistanceSlider . max = DEFAULT_SNAP_MAX ;
1035- repelSlider . max = DEFAULT_REPEL_MAX ; // Set initial repel max
1036- frequencySlider . max = 10 ; // Frequency max is constant
1111+ repelSlider . max = DEFAULT_REPEL_MAX ;
1112+ frequencySlider . max = 10 ;
10371113
10381114 speedSlider . value = animationSpeed ;
10391115 speedValueSpan . textContent = animationSpeed . toFixed ( 1 ) ;
@@ -1057,6 +1133,9 @@ <h2>Pixel Swirl</h2>
10571133
10581134 versionNumberSpan . textContent = `Version ${ APP_VERSION . toFixed ( 3 ) } ` ;
10591135 document . title = `Pixel Swirl Version ${ APP_VERSION . toFixed ( 3 ) } ` ;
1136+
1137+ // Initial setup for repel timing
1138+ lastRepelTime = performance . now ( ) ;
10601139 }
10611140 } ;
10621141
0 commit comments