diff --git a/source/funkin/data/stage/StageData.hx b/source/funkin/data/stage/StageData.hx
index bebd86d02..eda8e3148 100644
--- a/source/funkin/data/stage/StageData.hx
+++ b/source/funkin/data/stage/StageData.hx
@@ -140,12 +140,12 @@ typedef StageDataProp =
    * If not zero, this prop will play an animation every X beats of the song.
    * This requires animations to be defined. If `danceLeft` and `danceRight` are defined,
    * they will alternated between, otherwise the `idle` animation will be used.
-   *
-   * @default 0
+   * Supports up to 0.25 precision.
+   * @default 0.0
    */
-  @:default(0)
+  @:default(0.0)
   @:optional
-  var danceEvery:Int;
+  var danceEvery:Float;
 
   /**
    * How much the prop scrolls relative to the camera. Used to create a parallax effect.
diff --git a/source/funkin/data/story/level/LevelData.hx b/source/funkin/data/story/level/LevelData.hx
index ceb2cc054..d01689a82 100644
--- a/source/funkin/data/story/level/LevelData.hx
+++ b/source/funkin/data/story/level/LevelData.hx
@@ -91,11 +91,13 @@ typedef LevelPropData =
 
   /**
    * The frequency to bop at, in beats.
-   * @default 1 = every beat, 2 = every other beat, etc.
+   * 1 = every beat, 2 = every other beat, etc.
+   * Supports up to 0.25 precision.
+   * @default 0.0
    */
-  @:default(1)
+  @:default(0.0)
   @:optional
-  var danceEvery:Int;
+  var danceEvery:Float;
 
   /**
    * The offset on the position to render the prop at.
diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx
index 980cf2106..c228d803a 100644
--- a/source/funkin/play/character/BaseCharacter.hx
+++ b/source/funkin/play/character/BaseCharacter.hx
@@ -164,7 +164,7 @@ class BaseCharacter extends Bopper
 
   public function new(id:String, renderType:CharacterRenderType)
   {
-    super();
+    super(CharacterDataParser.DEFAULT_DANCEEVERY);
     this.characterId = id;
 
     _data = CharacterDataParser.fetchCharacterData(this.characterId);
@@ -180,6 +180,7 @@ class BaseCharacter extends Bopper
     {
       this.characterName = _data.name;
       this.name = _data.name;
+      this.danceEvery = _data.danceEvery;
       this.singTimeSteps = _data.singTime;
       this.globalOffsets = _data.offsets;
       this.flipX = _data.flipX;
diff --git a/source/funkin/play/character/CharacterData.hx b/source/funkin/play/character/CharacterData.hx
index 8b1649e26..d447eb97f 100644
--- a/source/funkin/play/character/CharacterData.hx
+++ b/source/funkin/play/character/CharacterData.hx
@@ -383,21 +383,21 @@ class CharacterDataParser
    * Values that are too high will cause the character to hold their singing pose for too long after they're done.
    * @default `8 steps`
    */
-  static final DEFAULT_SINGTIME:Float = 8.0;
+  public static final DEFAULT_SINGTIME:Float = 8.0;
 
-  static final DEFAULT_DANCEEVERY:Int = 1;
-  static final DEFAULT_FLIPX:Bool = false;
-  static final DEFAULT_FLIPY:Bool = false;
-  static final DEFAULT_FRAMERATE:Int = 24;
-  static final DEFAULT_ISPIXEL:Bool = false;
-  static final DEFAULT_LOOP:Bool = false;
-  static final DEFAULT_NAME:String = 'Untitled Character';
-  static final DEFAULT_OFFSETS:Array<Float> = [0, 0];
-  static final DEFAULT_HEALTHICON_OFFSETS:Array<Int> = [0, 25];
-  static final DEFAULT_RENDERTYPE:CharacterRenderType = CharacterRenderType.Sparrow;
-  static final DEFAULT_SCALE:Float = 1;
-  static final DEFAULT_SCROLL:Array<Float> = [0, 0];
-  static final DEFAULT_STARTINGANIM:String = 'idle';
+  public static final DEFAULT_DANCEEVERY:Float = 1.0;
+  public static final DEFAULT_FLIPX:Bool = false;
+  public static final DEFAULT_FLIPY:Bool = false;
+  public static final DEFAULT_FRAMERATE:Int = 24;
+  public static final DEFAULT_ISPIXEL:Bool = false;
+  public static final DEFAULT_LOOP:Bool = false;
+  public static final DEFAULT_NAME:String = 'Untitled Character';
+  public static final DEFAULT_OFFSETS:Array<Float> = [0, 0];
+  public static final DEFAULT_HEALTHICON_OFFSETS:Array<Int> = [0, 25];
+  public static final DEFAULT_RENDERTYPE:CharacterRenderType = CharacterRenderType.Sparrow;
+  public static final DEFAULT_SCALE:Float = 1;
+  public static final DEFAULT_SCROLL:Array<Float> = [0, 0];
+  public static final DEFAULT_STARTINGANIM:String = 'idle';
 
   /**
    * Set unspecified parameters to their defaults.
@@ -665,10 +665,12 @@ typedef CharacterData =
   /**
    * The frequency at which the character will play its idle animation, in beats.
    * Increasing this number will make the character dance less often.
-   *
-   * @default 1
+   * Supports up to `0.25` precision.
+   * @default `1.0` on characters
    */
-  var danceEvery:Null<Int>;
+  @:optional
+  @:default(1.0)
+  var danceEvery:Null<Float>;
 
   /**
    * The minimum duration that a character will play a note animation for, in beats.
diff --git a/source/funkin/play/stage/Bopper.hx b/source/funkin/play/stage/Bopper.hx
index 11fb9e499..0061e85fb 100644
--- a/source/funkin/play/stage/Bopper.hx
+++ b/source/funkin/play/stage/Bopper.hx
@@ -19,8 +19,10 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
   /**
    * The bopper plays the dance animation once every `danceEvery` beats.
    * Set to 0 to disable idle animation.
+   * Supports up to 0.25 precision.
+   * @default 0.0 on props, 1.0 on characters
    */
-  public var danceEvery:Int = 1;
+  public var danceEvery:Float = 0.0;
 
   /**
    * Whether the bopper should dance left and right.
@@ -110,7 +112,7 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
    */
   var hasDanced:Bool = false;
 
-  public function new(danceEvery:Int = 1)
+  public function new(danceEvery:Float = 0.0)
   {
     super();
     this.danceEvery = danceEvery;
@@ -171,16 +173,20 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
   }
 
   /**
-   * Called once every beat of the song.
+   * Called once every step of the song.
    */
-  public function onBeatHit(event:SongTimeScriptEvent):Void
+  public function onStepHit(event:SongTimeScriptEvent)
   {
-    if (danceEvery > 0 && event.beat % danceEvery == 0)
+    if (danceEvery > 0) trace('step hit(${danceEvery}): ${event.step % (danceEvery * Constants.STEPS_PER_BEAT)} == 0?');
+    if (danceEvery > 0 && (event.step % (danceEvery * Constants.STEPS_PER_BEAT)) == 0)
     {
+      trace('dance onStepHit!');
       dance(shouldBop);
     }
   }
 
+  public function onBeatHit(event:SongTimeScriptEvent):Void {}
+
   /**
    * Called every `danceEvery` beats of the song.
    */
@@ -367,8 +373,6 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
 
   public function onNoteGhostMiss(event:GhostMissNoteScriptEvent) {}
 
-  public function onStepHit(event:SongTimeScriptEvent) {}
-
   public function onCountdownStart(event:CountdownScriptEvent) {}
 
   public function onCountdownStep(event:CountdownScriptEvent) {}