or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-drawing.mdaudio-sound.mdcore-system.mddrawing-shapes.mdevent-input.mdgame-objects.mdgraphics-display.mdindex.mdinput-devices.mdjoystick-gamepad.mdmath-utils.mdsurface-image.mdtext-font.mdtime-animation.mdtransform-image.md

joystick-gamepad.mddocs/

0

# Joystick and Gamepad Control

1

2

Complete joystick and gamepad input system for handling controllers, analog sticks, buttons, and haptic feedback. Pygame's joystick module provides cross-platform support for game controllers with both basic input reading and advanced features like rumble feedback.

3

4

## Import Statements

5

6

```python

7

import pygame

8

import pygame.joystick

9

10

# Initialize joystick system

11

pygame.joystick.init()

12

```

13

14

## Capabilities

15

16

### System Initialization

17

18

Initialize and manage the joystick subsystem.

19

20

```python { .api }

21

def init() -> None:

22

"""

23

Initialize the joystick module.

24

Must be called before using joystick functions.

25

Safe to call multiple times.

26

"""

27

28

def quit() -> None:

29

"""

30

Uninitialize the joystick module.

31

Automatically called when pygame quits.

32

"""

33

34

def get_init() -> bool:

35

"""

36

Check if joystick module is initialized.

37

38

Returns:

39

bool: True if joystick module is initialized

40

"""

41

```

42

43

### Device Enumeration

44

45

Discover and count available joystick devices.

46

47

```python { .api }

48

def get_count() -> int:

49

"""

50

Get number of joysticks connected to the system.

51

52

Returns:

53

int: Number of available joystick devices

54

"""

55

```

56

57

### Joystick Control

58

59

Individual joystick device management and input reading.

60

61

```python { .api }

62

class Joystick:

63

def __init__(self, id: int):

64

"""

65

Create a Joystick object for device control.

66

67

Args:

68

id (int): Joystick device index (0 to get_count()-1)

69

"""

70

71

def init(self) -> None:

72

"""

73

Initialize the joystick device.

74

Must be called before reading input from this joystick.

75

"""

76

77

def quit(self) -> None:

78

"""

79

Uninitialize the joystick device.

80

Releases resources and stops input reading.

81

"""

82

83

def get_init(self) -> bool:

84

"""

85

Check if this joystick is initialized.

86

87

Returns:

88

bool: True if joystick is initialized and ready

89

"""

90

91

def get_id(self) -> int:

92

"""

93

Get the device index for this joystick.

94

95

Returns:

96

int: Device index used to create this joystick

97

"""

98

99

def get_instance_id(self) -> int:

100

"""

101

Get unique instance ID for this joystick.

102

Instance ID remains constant while device is connected.

103

104

Returns:

105

int: Unique instance identifier

106

"""

107

108

def get_name(self) -> str:

109

"""

110

Get the system name of the joystick device.

111

112

Returns:

113

str: Device name (e.g., "Xbox One Controller")

114

"""

115

116

def get_guid(self) -> str:

117

"""

118

Get unique identifier string for joystick type.

119

GUID is consistent across platforms for same controller model.

120

121

Returns:

122

str: Globally unique identifier string

123

"""

124

```

125

126

### Analog Input Reading

127

128

Read continuous analog values from sticks and triggers.

129

130

```python { .api }

131

class Joystick:

132

def get_numaxes(self) -> int:

133

"""

134

Get number of analog axes on this joystick.

135

136

Returns:

137

int: Number of analog axes (typically 2-6 for modern gamepads)

138

"""

139

140

def get_axis(self, axis: int) -> float:

141

"""

142

Get current value of an analog axis.

143

144

Args:

145

axis (int): Axis index (0 to get_numaxes()-1)

146

147

Returns:

148

float: Axis value (-1.0 to 1.0, 0.0 at rest)

149

"""

150

```

151

152

### Button Input Reading

153

154

Read digital button states from controller buttons.

155

156

```python { .api }

157

class Joystick:

158

def get_numbuttons(self) -> int:

159

"""

160

Get number of buttons on this joystick.

161

162

Returns:

163

int: Number of digital buttons

164

"""

165

166

def get_button(self, button: int) -> bool:

167

"""

168

Get current state of a button.

169

170

Args:

171

button (int): Button index (0 to get_numbuttons()-1)

172

173

Returns:

174

bool: True if button is currently pressed

175

"""

176

```

177

178

### D-Pad (Hat) Input Reading

179

180

Read directional pad input from hat switches.

181

182

```python { .api }

183

class Joystick:

184

def get_numhats(self) -> int:

185

"""

186

Get number of hat switches (D-pads) on this joystick.

187

188

Returns:

189

int: Number of hat switches (usually 0 or 1)

190

"""

191

192

def get_hat(self, hat: int) -> tuple[int, int]:

193

"""

194

Get current position of a hat switch.

195

196

Args:

197

hat (int): Hat index (0 to get_numhats()-1)

198

199

Returns:

200

tuple[int, int]: (x, y) values each -1, 0, or 1

201

(0, 0) = center, (0, 1) = up, (1, 0) = right, etc.

202

"""

203

```

204

205

### Advanced Features

206

207

Power management and haptic feedback capabilities (when supported).

208

209

```python { .api }

210

class Joystick:

211

def get_power_level(self) -> int:

212

"""

213

Get battery power level of wireless joystick.

214

Only available on some platforms and controllers.

215

216

Returns:

217

int: Power level constant or -1 if not supported

218

JOYSTICK_POWER_UNKNOWN = -1

219

JOYSTICK_POWER_EMPTY = 0

220

JOYSTICK_POWER_LOW = 1

221

JOYSTICK_POWER_MEDIUM = 2

222

JOYSTICK_POWER_FULL = 3

223

JOYSTICK_POWER_WIRED = 4

224

JOYSTICK_POWER_MAX = 5

225

"""

226

227

def rumble(self, low_freq: float, high_freq: float, duration: int) -> bool:

228

"""

229

Start haptic rumble feedback.

230

Only available on controllers with rumble support.

231

232

Args:

233

low_freq (float): Low frequency rumble intensity (0.0 to 1.0)

234

high_freq (float): High frequency rumble intensity (0.0 to 1.0)

235

duration (int): Rumble duration in milliseconds

236

237

Returns:

238

bool: True if rumble started successfully

239

"""

240

241

def stop_rumble(self) -> None:

242

"""

243

Stop all rumble feedback on this joystick.

244

Only available on controllers with rumble support.

245

"""

246

```

247

248

## Joystick Event Constants

249

250

Event types generated by joystick input changes.

251

252

```python { .api }

253

# Joystick events (from pygame.event)

254

JOYAXISMOTION: int # Analog axis value changed

255

JOYBUTTONDOWN: int # Button was pressed

256

JOYBUTTONUP: int # Button was released

257

JOYHATMOTION: int # Hat/D-pad direction changed

258

JOYDEVICEADDED: int # New joystick connected

259

JOYDEVICEREMOVED: int # Joystick disconnected

260

261

# Power level constants

262

JOYSTICK_POWER_UNKNOWN: int = -1 # Power level unknown

263

JOYSTICK_POWER_EMPTY: int = 0 # Battery empty

264

JOYSTICK_POWER_LOW: int = 1 # Battery low

265

JOYSTICK_POWER_MEDIUM: int = 2 # Battery medium

266

JOYSTICK_POWER_FULL: int = 3 # Battery full

267

JOYSTICK_POWER_WIRED: int = 4 # Wired connection

268

JOYSTICK_POWER_MAX: int = 5 # Maximum power level

269

```

270

271

### Event Attributes

272

273

Common attributes available in joystick events.

274

275

```python { .api }

276

# JOYAXISMOTION event attributes

277

event.joy: int # Joystick device index

278

event.axis: int # Axis number that moved

279

event.value: float # New axis value (-1.0 to 1.0)

280

281

# JOYBUTTONDOWN/JOYBUTTONUP event attributes

282

event.joy: int # Joystick device index

283

event.button: int # Button number pressed/released

284

285

# JOYHATMOTION event attributes

286

event.joy: int # Joystick device index

287

event.hat: int # Hat number that moved

288

event.value: tuple # (x, y) hat position (-1, 0, 1)

289

290

# JOYDEVICEADDED/JOYDEVICEREMOVED event attributes

291

event.device_index: int # Device index for added/removed joystick

292

```

293

294

## Usage Examples

295

296

### Basic Joystick Detection

297

298

```python

299

import pygame

300

301

pygame.init()

302

pygame.joystick.init()

303

304

print(f"Number of joysticks: {pygame.joystick.get_count()}")

305

306

# List all connected joysticks

307

joysticks = []

308

for i in range(pygame.joystick.get_count()):

309

joy = pygame.joystick.Joystick(i)

310

joy.init()

311

joysticks.append(joy)

312

313

print(f"Joystick {i}:")

314

print(f" Name: {joy.get_name()}")

315

print(f" GUID: {joy.get_guid()}")

316

print(f" Axes: {joy.get_numaxes()}")

317

print(f" Buttons: {joy.get_numbuttons()}")

318

print(f" Hats: {joy.get_numhats()}")

319

320

# Check power level if supported

321

try:

322

power = joy.get_power_level()

323

if power != -1:

324

power_names = ["Empty", "Low", "Medium", "Full", "Wired", "Max"]

325

print(f" Power: {power_names[power]}")

326

except AttributeError:

327

print(" Power: Not supported")

328

329

pygame.quit()

330

```

331

332

### Real-Time Gamepad Input

333

334

```python

335

import pygame

336

import sys

337

338

pygame.init()

339

pygame.joystick.init()

340

341

if pygame.joystick.get_count() == 0:

342

print("No joysticks connected!")

343

sys.exit()

344

345

# Initialize first joystick

346

joystick = pygame.joystick.Joystick(0)

347

joystick.init()

348

349

print(f"Using: {joystick.get_name()}")

350

351

screen = pygame.display.set_mode((800, 600))

352

pygame.display.set_caption("Gamepad Input Test")

353

clock = pygame.time.Clock()

354

355

# Player position controlled by joystick

356

player_x, player_y = 400, 300

357

player_speed = 5

358

359

running = True

360

while running:

361

# Handle events

362

for event in pygame.event.get():

363

if event.type == pygame.QUIT:

364

running = False

365

elif event.type == pygame.JOYBUTTONDOWN:

366

print(f"Button {event.button} pressed")

367

if event.button == 0: # Usually "A" button

368

print("Jump!")

369

elif event.type == pygame.JOYBUTTONUP:

370

print(f"Button {event.button} released")

371

elif event.type == pygame.JOYHATMOTION:

372

print(f"Hat {event.hat} moved to {event.value}")

373

374

# Read analog stick for movement (usually axes 0 and 1)

375

if joystick.get_numaxes() >= 2:

376

x_axis = joystick.get_axis(0) # Left stick X

377

y_axis = joystick.get_axis(1) # Left stick Y

378

379

# Apply deadzone to prevent drift

380

deadzone = 0.1

381

if abs(x_axis) > deadzone:

382

player_x += x_axis * player_speed

383

if abs(y_axis) > deadzone:

384

player_y += y_axis * player_speed

385

386

# Keep player on screen

387

player_x = max(25, min(775, player_x))

388

player_y = max(25, min(575, player_y))

389

390

# Draw everything

391

screen.fill((50, 50, 50))

392

pygame.draw.circle(screen, (255, 255, 255), (int(player_x), int(player_y)), 25)

393

394

# Draw axis indicators

395

if joystick.get_numaxes() >= 2:

396

stick_x = 100 + joystick.get_axis(0) * 50

397

stick_y = 100 + joystick.get_axis(1) * 50

398

pygame.draw.circle(screen, (0, 255, 0), (int(stick_x), int(stick_y)), 10)

399

pygame.draw.circle(screen, (100, 100, 100), (100, 100), 50, 2)

400

401

pygame.display.flip()

402

clock.tick(60)

403

404

pygame.quit()

405

```

406

407

### Advanced Gamepad Features

408

409

```python

410

import pygame

411

import sys

412

import math

413

414

pygame.init()

415

pygame.joystick.init()

416

417

if pygame.joystick.get_count() == 0:

418

print("No joysticks connected!")

419

sys.exit()

420

421

joystick = pygame.joystick.Joystick(0)

422

joystick.init()

423

424

screen = pygame.display.set_mode((800, 600))

425

pygame.display.set_caption("Advanced Gamepad Features")

426

clock = pygame.time.Clock()

427

428

# Game state

429

player_x, player_y = 400, 300

430

rumble_timer = 0

431

trigger_threshold = 0.5

432

433

running = True

434

while running:

435

for event in pygame.event.get():

436

if event.type == pygame.QUIT:

437

running = False

438

elif event.type == pygame.JOYBUTTONDOWN:

439

if event.button == 0: # A button - trigger rumble

440

try:

441

# Rumble for 500ms with medium intensity

442

if joystick.rumble(0.7, 0.3, 500):

443

print("Rumble started")

444

rumble_timer = 500

445

else:

446

print("Rumble not supported")

447

except AttributeError:

448

print("Rumble not available on this platform")

449

450

elif event.button == 1: # B button - stop rumble

451

try:

452

joystick.stop_rumble()

453

print("Rumble stopped")

454

rumble_timer = 0

455

except AttributeError:

456

pass

457

458

# Read all axes for advanced control

459

left_stick_x = joystick.get_axis(0) if joystick.get_numaxes() > 0 else 0

460

left_stick_y = joystick.get_axis(1) if joystick.get_numaxes() > 1 else 0

461

462

# Right stick for rotation (if available)

463

right_stick_x = joystick.get_axis(3) if joystick.get_numaxes() > 3 else 0

464

right_stick_y = joystick.get_axis(4) if joystick.get_numaxes() > 4 else 0

465

466

# Triggers (if available)

467

left_trigger = joystick.get_axis(2) if joystick.get_numaxes() > 2 else 0

468

right_trigger = joystick.get_axis(5) if joystick.get_numaxes() > 5 else 0

469

470

# Movement with deadzone

471

deadzone = 0.15

472

if abs(left_stick_x) > deadzone or abs(left_stick_y) > deadzone:

473

# Calculate magnitude for smooth movement

474

magnitude = math.sqrt(left_stick_x**2 + left_stick_y**2)

475

if magnitude > 1.0:

476

magnitude = 1.0

477

478

# Apply magnitude-based speed

479

speed = magnitude * 8

480

player_x += left_stick_x * speed

481

player_y += left_stick_y * speed

482

483

# Keep player on screen

484

player_x = max(25, min(775, player_x))

485

player_y = max(25, min(575, player_y))

486

487

# Calculate rotation from right stick

488

rotation = 0

489

if abs(right_stick_x) > deadzone or abs(right_stick_y) > deadzone:

490

rotation = math.atan2(right_stick_y, right_stick_x)

491

492

# Update rumble timer

493

if rumble_timer > 0:

494

rumble_timer -= clock.get_time()

495

496

# Draw everything

497

screen.fill((30, 30, 30))

498

499

# Draw player with rotation indicator

500

player_color = (255, 255, 255)

501

if left_trigger > trigger_threshold:

502

player_color = (255, 100, 100) # Red when left trigger pressed

503

elif right_trigger > trigger_threshold:

504

player_color = (100, 100, 255) # Blue when right trigger pressed

505

506

pygame.draw.circle(screen, player_color, (int(player_x), int(player_y)), 25)

507

508

# Draw rotation indicator

509

if rotation != 0:

510

end_x = player_x + math.cos(rotation) * 40

511

end_y = player_y + math.sin(rotation) * 40

512

pygame.draw.line(screen, (255, 255, 0), (player_x, player_y), (end_x, end_y), 3)

513

514

# Draw stick visualizers

515

# Left stick

516

stick_center = (100, 500)

517

pygame.draw.circle(screen, (100, 100, 100), stick_center, 50, 2)

518

stick_pos = (stick_center[0] + left_stick_x * 40, stick_center[1] + left_stick_y * 40)

519

pygame.draw.circle(screen, (0, 255, 0), stick_pos, 8)

520

521

# Right stick

522

stick_center = (700, 500)

523

pygame.draw.circle(screen, (100, 100, 100), stick_center, 50, 2)

524

stick_pos = (stick_center[0] + right_stick_x * 40, stick_center[1] + right_stick_y * 40)

525

pygame.draw.circle(screen, (255, 0, 255), stick_pos, 8)

526

527

# Draw trigger indicators

528

trigger_width = 100

529

trigger_height = 20

530

531

# Left trigger

532

left_bar_width = int((left_trigger + 1) * 0.5 * trigger_width) # Convert -1,1 to 0,1

533

pygame.draw.rect(screen, (100, 100, 100), (50, 50, trigger_width, trigger_height), 2)

534

if left_bar_width > 0:

535

pygame.draw.rect(screen, (255, 0, 0), (50, 50, left_bar_width, trigger_height))

536

537

# Right trigger

538

right_bar_width = int((right_trigger + 1) * 0.5 * trigger_width)

539

pygame.draw.rect(screen, (100, 100, 100), (650, 50, trigger_width, trigger_height), 2)

540

if right_bar_width > 0:

541

pygame.draw.rect(screen, (0, 0, 255), (650, 50, right_bar_width, trigger_height))

542

543

# Show rumble status

544

if rumble_timer > 0:

545

font = pygame.font.Font(None, 36)

546

text = font.render("RUMBLING", True, (255, 255, 0))

547

screen.blit(text, (350, 100))

548

549

# Hat/D-pad visualization

550

if joystick.get_numhats() > 0:

551

hat_value = joystick.get_hat(0)

552

hat_center = (400, 500)

553

pygame.draw.circle(screen, (150, 150, 150), hat_center, 30, 2)

554

555

if hat_value != (0, 0):

556

hat_pos = (hat_center[0] + hat_value[0] * 20, hat_center[1] - hat_value[1] * 20)

557

pygame.draw.circle(screen, (255, 255, 0), hat_pos, 10)

558

559

pygame.display.flip()

560

clock.tick(60)

561

562

pygame.quit()

563

```

564

565

### Gamepad Hot-Plugging Support

566

567

```python

568

import pygame

569

import sys

570

571

pygame.init()

572

pygame.joystick.init()

573

574

screen = pygame.display.set_mode((800, 600))

575

pygame.display.set_caption("Gamepad Hot-Plug Support")

576

clock = pygame.time.Clock()

577

578

# Track active joysticks

579

joysticks = {}

580

581

def add_joystick(device_index):

582

"""Add a new joystick when connected."""

583

try:

584

joystick = pygame.joystick.Joystick(device_index)

585

joystick.init()

586

joysticks[device_index] = joystick

587

print(f"Joystick {device_index} connected: {joystick.get_name()}")

588

except pygame.error as e:

589

print(f"Failed to initialize joystick {device_index}: {e}")

590

591

def remove_joystick(device_index):

592

"""Remove a joystick when disconnected."""

593

if device_index in joysticks:

594

joystick = joysticks[device_index]

595

print(f"Joystick {device_index} disconnected: {joystick.get_name()}")

596

joystick.quit()

597

del joysticks[device_index]

598

599

# Initialize any connected joysticks

600

for i in range(pygame.joystick.get_count()):

601

add_joystick(i)

602

603

running = True

604

while running:

605

for event in pygame.event.get():

606

if event.type == pygame.QUIT:

607

running = False

608

609

elif event.type == pygame.JOYDEVICEADDED:

610

print(f"Device added at index {event.device_index}")

611

add_joystick(event.device_index)

612

613

elif event.type == pygame.JOYDEVICEREMOVED:

614

print(f"Device removed at index {event.device_index}")

615

remove_joystick(event.device_index)

616

617

elif event.type == pygame.JOYBUTTONDOWN:

618

joystick = joysticks.get(event.joy)

619

if joystick:

620

print(f"Button {event.button} pressed on {joystick.get_name()}")

621

622

elif event.type == pygame.JOYAXISMOTION:

623

# Only print significant axis movements

624

if abs(event.value) > 0.5:

625

joystick = joysticks.get(event.joy)

626

if joystick:

627

print(f"Axis {event.axis} = {event.value:.2f} on {joystick.get_name()}")

628

629

screen.fill((40, 40, 40))

630

631

# Display connected joysticks

632

font = pygame.font.Font(None, 36)

633

y_offset = 50

634

635

if not joysticks:

636

text = font.render("No joysticks connected", True, (255, 255, 255))

637

screen.blit(text, (50, y_offset))

638

text = font.render("Connect a gamepad to test hot-plugging", True, (200, 200, 200))

639

screen.blit(text, (50, y_offset + 40))

640

else:

641

text = font.render(f"Connected joysticks: {len(joysticks)}", True, (255, 255, 255))

642

screen.blit(text, (50, y_offset))

643

y_offset += 50

644

645

for device_index, joystick in joysticks.items():

646

text = font.render(f"{device_index}: {joystick.get_name()}", True, (200, 255, 200))

647

screen.blit(text, (70, y_offset))

648

y_offset += 30

649

650

# Show some live input data

651

if joystick.get_numaxes() >= 2:

652

x_axis = joystick.get_axis(0)

653

y_axis = joystick.get_axis(1)

654

text = font.render(f" Stick: ({x_axis:.2f}, {y_axis:.2f})", True, (150, 150, 150))

655

screen.blit(text, (90, y_offset))

656

y_offset += 25

657

658

# Show pressed buttons

659

pressed_buttons = []

660

for i in range(joystick.get_numbuttons()):

661

if joystick.get_button(i):

662

pressed_buttons.append(str(i))

663

664

if pressed_buttons:

665

text = font.render(f" Buttons: {', '.join(pressed_buttons)}", True, (255, 255, 0))

666

screen.blit(text, (90, y_offset))

667

y_offset += 40

668

669

pygame.display.flip()

670

clock.tick(60)

671

672

# Clean up all joysticks

673

for joystick in joysticks.values():

674

joystick.quit()

675

676

pygame.quit()

677

```

678

679

This documentation provides comprehensive coverage of the pygame.joystick module, following the same high-quality structure as the existing pygame documentation. It includes all the API functions with proper type hints, detailed explanations, practical examples, and follows the established format with `{ .api }` blocks and JSDoc-style parameter documentation.