Skip to content

Instantly share code, notes, and snippets.

@xyproto
Last active October 8, 2025 13:12
Show Gist options
  • Save xyproto/be6e8ca8448917cd1396b8b83a8c85af to your computer and use it in GitHub Desktop.
Save xyproto/be6e8ca8448917cd1396b8b83a8c85af to your computer and use it in GitHub Desktop.
Make the mouse cursor move in boid-like ways, for Windows (shake to pause)
package main
import (
"fmt"
"math"
"syscall"
"time"
"unsafe"
)
type POINT struct {
X, Y int32
}
type RECT struct {
Left, Top, Right, Bottom int32
}
type Boid struct {
X, Y float64
VelX, VelY float64
}
type MovementEvent struct {
deltaX float64
time time.Time
}
var (
globalStartTime time.Time
cursorBoid Boid
maxSpeed float64 = 5.0
maxForce float64 = 0.2
orbitRadius float64 = 40.0
screenBounds RECT
)
var (
user32 = syscall.NewLazyDLL("user32.dll")
setCursorPosProc = user32.NewProc("SetCursorPos")
getCursorPosProc = user32.NewProc("GetCursorPos")
getSystemMetricsProc = user32.NewProc("GetSystemMetrics")
getDesktopWindowProc = user32.NewProc("GetDesktopWindow")
getWindowRectProc = user32.NewProc("GetWindowRect")
)
const (
SM_XVIRTUALSCREEN = 76
SM_YVIRTUALSCREEN = 77
SM_CXVIRTUALSCREEN = 78
SM_CYVIRTUALSCREEN = 79
)
func SetCursorPos(x, y int) bool {
ret, _, _ := syscall.SyscallN(setCursorPosProc.Addr(), uintptr(x), uintptr(y))
return ret != 0
}
func GetCursorPos() (POINT, error) {
var pt POINT
ret, _, err := syscall.SyscallN(getCursorPosProc.Addr(), uintptr(unsafe.Pointer(&pt)))
if ret == 0 {
return POINT{}, fmt.Errorf("failed to get cursor position: %v", err)
}
return pt, nil
}
func getVirtualScreenBounds() RECT {
x, _, _ := getSystemMetricsProc.Call(SM_XVIRTUALSCREEN)
y, _, _ := getSystemMetricsProc.Call(SM_YVIRTUALSCREEN)
w, _, _ := getSystemMetricsProc.Call(SM_CXVIRTUALSCREEN)
h, _, _ := getSystemMetricsProc.Call(SM_CYVIRTUALSCREEN)
return RECT{
Left: int32(x),
Top: int32(y),
Right: int32(x) + int32(w) - 1,
Bottom: int32(y) + int32(h) - 1,
}
}
func constrainToBounds(x, y float64) (float64, float64) {
if x < float64(screenBounds.Left) {
x = float64(screenBounds.Left)
}
if x > float64(screenBounds.Right) {
x = float64(screenBounds.Right)
}
if y < float64(screenBounds.Top) {
y = float64(screenBounds.Top)
}
if y > float64(screenBounds.Bottom) {
y = float64(screenBounds.Bottom)
}
return x, y
}
func normalize(x, y float64) (float64, float64) {
mag := math.Sqrt(x*x + y*y)
if mag == 0 {
return 0, 0
}
return x / mag, y / mag
}
func limit(x, y, maxMag float64) (float64, float64) {
mag := math.Sqrt(x*x + y*y)
if mag > maxMag {
return (x / mag) * maxMag, (y / mag) * maxMag
}
return x, y
}
func (b *Boid) seek(targetX, targetY float64) (float64, float64) {
desiredX := targetX - b.X
desiredY := targetY - b.Y
desiredX, desiredY = normalize(desiredX, desiredY)
desiredX *= maxSpeed
desiredY *= maxSpeed
steerX := desiredX - b.VelX
steerY := desiredY - b.VelY
return limit(steerX, steerY, maxForce)
}
func (b *Boid) orbit(targetX, targetY float64, userMovementSpeed float64) (float64, float64) {
toTargetX := b.X - targetX
toTargetY := b.Y - targetY
distance := math.Sqrt(toTargetX*toTargetX + toTargetY*toTargetY)
dynamicRadius := orbitRadius * (0.5 + userMovementSpeed*0.15)
if dynamicRadius > 100 {
dynamicRadius = 100
}
var steerX, steerY float64
if distance < dynamicRadius*0.6 {
if distance > 0 {
awayX, awayY := normalize(toTargetX, toTargetY)
steerX, steerY = b.seek(b.X+awayX*dynamicRadius*1.5, b.Y+awayY*dynamicRadius*1.5)
}
} else if distance > dynamicRadius*1.8 {
steerX, steerY = b.seek(targetX, targetY)
} else {
perpX := -toTargetY
perpY := toTargetX
perpX, perpY = normalize(perpX, perpY)
tangentialSpeed := maxSpeed * 0.9
desiredX := perpX * tangentialSpeed
desiredY := perpY * tangentialSpeed
timeFloat := float64(time.Now().UnixNano()) / 1000000000.0
randomFactorX := 1.2
randomFactorY := 0.8
desiredX += (math.Sin(timeFloat*0.7) * randomFactorX)
desiredY += (math.Cos(timeFloat*1.3) * randomFactorY)
desiredX += (math.Sin(timeFloat*2.1) * 0.5)
desiredY += (math.Cos(timeFloat*2.7) * 0.5)
steerX = desiredX - b.VelX
steerY = desiredY - b.VelY
steerX, steerY = limit(steerX, steerY, maxForce)
}
return steerX, steerY
}
func (b *Boid) update(steerX, steerY float64) {
b.VelX += steerX
b.VelY += steerY
b.VelX, b.VelY = limit(b.VelX, b.VelY, maxSpeed)
b.X += b.VelX
b.Y += b.VelY
b.X, b.Y = constrainToBounds(b.X, b.Y)
}
func detectShake(recentMovements []MovementEvent, currentTime time.Time) bool {
cutoffTime := currentTime.Add(-500 * time.Millisecond)
hasRapidLeft := false
hasRapidRight := false
for _, movement := range recentMovements {
if movement.time.Before(cutoffTime) {
continue
}
if movement.deltaX > 60 {
hasRapidRight = true
} else if movement.deltaX < -60 {
hasRapidLeft = true
}
if hasRapidLeft && hasRapidRight {
return true
}
}
return false
}
func main() {
delay := 16 * time.Millisecond
movementThreshold := 3.0
screenBounds = getVirtualScreenBounds()
globalStartTime = time.Now()
initialPos, err := GetCursorPos()
if err != nil {
fmt.Printf("Error getting initial cursor position: %v\n", err)
return
}
cursorBoid = Boid{
X: float64(initialPos.X),
Y: float64(initialPos.Y),
VelX: 0,
VelY: 0,
}
virtualCenterX := float64(initialPos.X)
virtualCenterY := float64(initialPos.Y)
virtualCenterX, virtualCenterY = constrainToBounds(virtualCenterX, virtualCenterY)
recentMovements := make([]float64, 8)
movementIndex := 0
recentHorizontalMovements := make([]MovementEvent, 20)
movementEventIndex := 0
pauseUntil := time.Time{}
for {
currentTime := time.Now()
if currentTime.Before(pauseUntil) {
time.Sleep(delay)
continue
}
if currentTime.Equal(pauseUntil) || (pauseUntil != time.Time{} && !currentTime.Before(pauseUntil) && pauseUntil.Before(currentTime.Add(-delay))) {
currentPos, err := GetCursorPos()
if err == nil {
virtualCenterX = float64(currentPos.X)
virtualCenterY = float64(currentPos.Y)
virtualCenterX, virtualCenterY = constrainToBounds(virtualCenterX, virtualCenterY)
cursorBoid.X = virtualCenterX
cursorBoid.Y = virtualCenterY
cursorBoid.VelX = 0
cursorBoid.VelY = 0
}
pauseUntil = time.Time{}
}
actualPos, err := GetCursorPos()
if err != nil {
fmt.Printf("Error getting cursor position: %v\n", err)
time.Sleep(delay)
continue
}
actualX := float64(actualPos.X)
actualY := float64(actualPos.Y)
expectedX := cursorBoid.X
expectedY := cursorBoid.Y
deltaX := actualX - expectedX
deltaY := actualY - expectedY
movementMagnitude := math.Sqrt(deltaX*deltaX + deltaY*deltaY)
if movementMagnitude > movementThreshold {
virtualCenterX += deltaX
virtualCenterY += deltaY
virtualCenterX, virtualCenterY = constrainToBounds(virtualCenterX, virtualCenterY)
recentMovements[movementIndex] = math.Sqrt(deltaX*deltaX + deltaY*deltaY)
movementIndex = (movementIndex + 1) % len(recentMovements)
recentHorizontalMovements[movementEventIndex] = MovementEvent{
deltaX: deltaX,
time: currentTime,
}
movementEventIndex = (movementEventIndex + 1) % len(recentHorizontalMovements)
if detectShake(recentHorizontalMovements, currentTime) {
pauseUntil = currentTime.Add(10 * time.Second)
for i := range recentHorizontalMovements {
recentHorizontalMovements[i] = MovementEvent{}
}
continue
}
}
var avgMovementSpeed float64 = 0
for _, movement := range recentMovements {
avgMovementSpeed += movement
}
avgMovementSpeed /= float64(len(recentMovements))
maxSpeed = 3.0 + avgMovementSpeed*0.4
if maxSpeed > 8.0 {
maxSpeed = 8.0
}
maxForce = 0.15 + avgMovementSpeed*0.03
if maxForce > 0.4 {
maxForce = 0.4
}
steerX, steerY := cursorBoid.orbit(virtualCenterX, virtualCenterY, avgMovementSpeed)
cursorBoid.update(steerX, steerY)
finalX, finalY := constrainToBounds(cursorBoid.X, cursorBoid.Y)
if !SetCursorPos(int(finalX), int(finalY)) {
fmt.Println("Error setting cursor position. Try running as administrator.")
}
time.Sleep(delay)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment