The simplest alternative to sinusoidal encoding: just learn a per-position embedding from scratch, the same way you learn token embeddings. The embedding matrix has shape (max_seq_len × d_model). Position 0 has its own row, position 1 has its own row, and so on.
BERT and GPT-2 use this. It is straightforward to implement, behaves predictably during training, and can capture position-dependent patterns that sinusoidal encoding cannot (e.g., "position 0 always gets a [CLS] token, so its embedding can specialize"). The cost: an extra max_seq_len × d_model parameters, and — critically — no extrapolation past max_seq_len. A model trained with max_seq_len=1024 has no embedding for position 1500.
This hard cap is why both BERT (max 512) and GPT-2 (max 1024) had relatively short context windows for their era. To get longer contexts you either (a) pre-train with longer sequences from the start, (b) interpolate the position embeddings post-hoc and fine-tune (the "Position Interpolation" technique), or (c) use a different positional scheme entirely. Modern LLMs chose (c).