Skip to main content

Quranic Phonemizer

PyPI version Website Paper License

Quranic Grapheme-to-Phoneme (G2P) converter and tajweed annotator for the riwayat of Hafs 'An Asim and Warsh 'An Nafi' that converts text to phoneme sequences with comprehensive support for waqf/ibtidaa transformations and tajweed breakdowns.

Use cases:

  • Speech Recognition: Phonetically transcribe recitations, create training data for machine learning systems
  • Text-to-Speech: Develop accurate TTS systems for Qurʾanic Arabic
  • Linguistic & Tajweed Analysis: Study phonological patterns and tajweed rule distributions across the Qurʾan, apply tajweed rule labels and colors
  • Educational Tools: Build interactive applications for assessing Quranic and tajweed pronunciation
  • Timing Analysis: Generate word-by-word timestamps for recitations, analyse madd/ghunnah durations

Table of Contents

Phoneme Inventory

The phoneme inventory uses the standard International Phonetic Alphabet (IPA) Arabic phonemes alongside custom phonemes for Tajweed rules. Hafs has 68 base phonemes plus 5 optional tokens; Warsh has 71 base phonemes plus 5 optional tokens.

Foundational Phonemes

Letter Phoneme Letter Phoneme Letter Phoneme Letter Phoneme
أ , إ , ء , ؤ , ئ ʔ د d / dd ض dˤ / dˤdˤ ك k / kk
ب b / bb ذ ð / ðð ط tˤ / tˤtˤ ل l / lˤ / ll / lˤlˤ
ت t / tt ر r / rˤ / rr / rˤrˤ ظ ðˤ / ðˤðˤ م m
ث θ / θθ ز z / zz ع ʕ / ʕʕ ن n
ج ʒ / ʒʒ س s / ss غ ɣ هـ h / hh
ح ħ / ħħ ش ʃ / ʃʃ ف f / ff و w / ww
خ x / xx ص sˤ / sˤsˤ ق q / qq ي , ى j / jj

lˤ is the single emphatic lam used in Warsh. Gemination (shaddah) is represented by repeating the phoneme to create distinct phonemes. Note that there is no gemination for m / n (modelled as tajweed instead), and for ʔ / ɣ (do not exist in the Qurʾān).

Vowel Phonemes

Vowel Phoneme
َ a
ُ u
ِ i
ا , ى aː / aˤː
و uː
ي , ى iː
Warsh Taqlil (Imala Sughra) ɛ / ɛː

Tajweed Phonemes

Rule Phoneme
Ikhfaa ŋ
Ikhfaa shafawi ŋ / m̃
Iqlab ŋ / m̃
Idgham ñ / m̃ / j̃ / w̃
Qalqala Q

Iqlab and ikhfaa shafawi use the open-lip nasal ŋ by default. Their nasal variants can instead select the closed-lip bilabial realization m̃, since both alternatives exist in recitations (see Variants).

Extra Phonemes

extra_phonemes selects toggleable output distinctions. Its default is empty to keep the default inventory compact; the underlying reading rule is unchanged.

API option Phoneme Default Notes
emphatic_fatha aˤ Off Allophone
emphatic_ikhfaa ŋˤ Off Heavy nasal allophone before the 5 ikhfaa-isti'la letters
qalqala_degree QQ Off Stronger Qalqala kubra/akbar allophone
imala (kubra) e: Off Hafs: مَجْر۪ىٰهَا (11:41), off -> i:
Warsh: طَه۪ (20:1), off -> ɛ:
tashil (Hafs only) ʔ̞ Off One case Hafs: ءَا۬عْجَمِيٌّ (41:44), off -> ʔ
Warsh always applies Tashil as ʔ̞ since it is common
taqlil_short (Warsh only) ɛ Off The short taqlil on the raa of the fixed رأى family, off -> a
The taqlil rule and light raa always apply

Quick start

Install the package and create a reader for the riwayah you need:

pip install quranic-phonemizer
from quranic_phonemizer import Phonemizer

hafs = Phonemizer()
warsh = Phonemizer(riwayah="warsh")

for name, reader, ref in (
    ("Hafs 1:4", hafs, "1:4"),
    ("Warsh 1:3", warsh, "1:3"),
):
    result = reader.analyse(ref)
    print(name)
    print(result.text())
    print(" ".join(result.phonemes()))
Hafs 1:4
مَـٰلِكِ يَوْمِ ٱلدِّينِ
m a: l i k i j a w m i dd i: n

Warsh 1:3
مَلِكِ يَوْمِ اِ۬لدِّينِۖ
m a l i k i j a w m i dd i: n

References

analyse() accepts words, verses, surahs, and ranges:

Reference Selection
"1" Surah 1
"1:3" Ayah 3 of surah 1
"1:3:1" Word 1 of ayah 1:3
"1:3-1:4" Ayahs 1:3 through 1:4
"1:3-1:4:2" Ayah 1:3 through word 2 of 1:4
"1-2:2" Surah 1 through ayah 2:2

Waqf

Use verse to stop at every ayah end in a range. Mushaf sign keys and exact word references can add other stops:

hafs.analyse("68:33", stop_signs=("optional_stop",))
hafs.analyse("2:255", stop_refs=("2:255:7",))

This applies waqf to 2:255:7 and ibtidaa to 2:255:8, or to any words marked with ۚ in 68:33, changing phonemes and tajweed rules accordingly.

Note the first and last word of a request always apply ibtidaa and waqf respectively.

reader.available_stop_signs gives the keys valid for that reader. Hafs uses:

Stop key Sign
verse ۝
preferred_continue ۖ
preferred_stop ۗ
optional_stop ۚ
compulsory_stop ۘ
prohibited_stop ۙ
either_stop ۛ

Warsh exposes only optional_stop ۖ

Analysis

The phonemizer exposes more detailed analysis, breakdowns, relationships and rules, as shown below. See the public API reference for the full details.

result = hafs.analyse("112:2")

print(result.text())
print(" ".join(result.phonemes()))
print(sorted({occurrence.rule_id.value for occurrence in result.rule_occurrences}))
ٱللَّهُ ٱلصَّمَدُ
ʔ a lˤlˤ aˤ: h u sˤsˤ a m a d Q
['hamza_wasl_fatha', 'hamza_wasl_silent', 'lam_shamsiyyah', 'madd_tabii',
 'qalqala_kubra', 'tafkheem', 'waqf_diacritic_drop']

The core records are available directly:

result.words
result.boundaries
result.sounds
result.rule_occurrences
result.mergers

The same result provides its source units, highlight groups, and transformed cells:

source = result.source()
highlights = result.highlights()
cells = result.cells(spelling="transformed")

The cell view is the render-ready alignment between transformed spelling, phonemes, and tajweed. It informs what to draw, what changed, where a sound is presented, and which rules belong to each cell:

Word Transformed cell Role and placement Status Sound relationship Rules
ٱللَّهُ أ Letter on the main row Replaced Owns ʔ hamza_wasl_fatha
ٱللَّهُ َ Haraka above أ; inserted after it Inserted Owns a —
ٱللَّهُ ل Letter on the main row Present Presents lˤlˤ with the next cell lam_shamsiyyah
ٱللَّهُ لّ Letter on the main row Present Owns the shared lˤlˤ tafkheem
ٱللَّهُ َ Haraka above لّ Present Presents aˤː with the following madd cell tafkheem
ٱللَّهُ ٰ Madd inserted after the fatha Inserted Owns the shared aˤː madd_tabii, tafkheem
ٱللَّهُ ه Letter on the main row Present Owns h —
ٱللَّهُ ُ Haraka above ه Present Owns u —
ٱلصَّمَدُ ٱ Letter on the main row Dropped No phoneme hamza_wasl_silent
ٱلصَّمَدُ ل Letter on the main row Present Presents sˤsˤ with the next cell lam_shamsiyyah
ٱلصَّمَدُ صّ Letter on the main row Present Owns the shared sˤsˤ tafkheem
ٱلصَّمَدُ َ Haraka above صّ Present Owns a tafkheem
ٱلصَّمَدُ م Letter on the main row Present Owns m —
ٱلصَّمَدُ َ Haraka above م Present Owns a —
ٱلصَّمَدُ دْ Letter on the main row Replaced Owns d and the qalqala release Q qalqala_kubra
ٱلصَّمَدُ ُ Haraka above د Dropped No phoneme waqf_diacritic_drop

document() returns JSON-compatible schema 2 documents for the analysis and its projections.

Tajweed rules

The catalogue is scoped to the reader. Each definition provides an ID, English name, Arabic name, and summary:

for rule in hafs.tajweed_rules:
    print(rule.id.value, rule.name, rule.arabic_name, rule.summary)

result.rule_definition("idgham_bila_ghunnah")
result.rule_occurrences

rule_occurrences contains the rules applied to that request. Hafs and Warsh share these published rule IDs:

  • Noon / Meem: izhar, izhar_shafawi, ikhfaa, ikhfaa_shafawi, iqlab, idgham_bi_ghunnah, idgham_bila_ghunnah, idgham_shafawi, ghunnah_mushaddadah
  • Assimilation and definite articles: idgham_mutamathilayn, idgham_mutaqaribayn, idgham_mutajanisayn_kamil, idgham_mutajanisayn_naqis, lam_shamsiyyah, lam_qamariyyah
  • Qalqala and Emphasis: qalqala_sughra, qalqala_kubra, qalqala_akbar, tafkheem, tarqeeq
  • Madd: madd_tabii, madd_muttasil, madd_munfasil, madd_lazim, madd_arid_lissukun, madd_leen, madd_iwad, madd_badal, madd_silah
  • Hamza and adjacent sakin letters: ibdal_hamza, hamza_wasl_silent, hamza_wasl_fatha, hamza_wasl_damma, hamza_wasl_kasra, iltiqa_haraka, iltiqa_shortening
  • Waqf and silence: waqf_diacritic_drop, waqf_silah_drop, waqf_taa_marbuta, pausal_alif (seven alifs), orthographic_silence (rasm)
  • Special: imala, tashil, ishmam

Warsh adds five unique rules:

  • taqlil
  • naql
  • madd_leen_mahmuz
  • madd_mim_al_jam
  • madd_yaa_zawaid

Variants

A riwayah carries authenticated khilaf in awjuh and turuq: places where more than one performance is transmitted for the same text. These are lexical words, systematic patterns, and junctions between words. Each variant is one selector taking one scalar value, chosen at construction:

from quranic_phonemizer import Phonemizer, available_variants

available_variants("hafs")["man_raq"]

sakt = Phonemizer(variants={"man_raq": "sakt"})
idraj = Phonemizer(variants={"man_raq": "idraj"})

sakt_res = sakt.analyse("75:27")
idraj_res = idraj.analyse("75:27")

print(sakt_res.text())
print(" ".join(sakt_res.phonemes()))
print(" ".join(idraj_res.phonemes()))
وَقِيلَ مَنْ ۜ رَاقٍ

w a q i: l a m a n rˤ aˤ: q Q
w a q i: l a m a rˤrˤ aˤ: q Q

available_variants(riwayah) gives the legal values and the default for each ID, variant_catalogue(riwayah) adds presentation and applicability metadata, and result.variant_occurrences() reports the sites contained in one analysis. See the variants contract for details, options, scopes, examples and research sources.

Hafs publishes 26 selectors:

  • Word readings: daaf_haraka, yabsut, bastah, almusaytirun, bimusaytir, istifham_article, tamanna_noon
  • Joined readings: noon_wasl, yaseen_wasl, irkab_maana, yalhath_dhalik
  • Stopping and starting: yaa_aatani_waqf, salasila_waqf, alism_ibtidaa
  • Sakt: maliyah_halak, iwaja_qayyima, man_raq, bal_ran
  • Raa: raa_firq, raa_alqitr_waqf, raa_misr_waqf, raa_wanuthur_waqf, raa_yasr_waqf, raa_asr_waqf
  • Nasal: iqlab_nasal, ikhfaa_shafawi_nasal

Warsh publishes 57:

  • Word readings: tamanna_noon, istifham_article
  • Joined readings: noon_wasl, maliyah_halak, kitabiyah_inni
  • Stopping and starting: article_ibtidaa
  • Inclination: dhat_yaa, arakahum, al_jar, jabbarin, haa_verse_heads, maryam_haa_yaa, yaseen_yaa
  • Hamza: hamza_dhat_fath, hamza_muttafiq, hamza_damm_kasr, jaa_aal, hamza_kasr_yaa, hamza_aimma, hamza_arayta, ha_antum, allai_waqf
  • Lam: lam_dhat_yaa, lam_verse_heads, lam_separated_by_alif, lam_final_waqf, lam_salsal, lam_after_taa, lam_after_zhaa
  • Raa: the six Hafs raa selectors, plus raa_ishruna_kibr, raa_alishraq, raa_hayran, raa_bisharar, raa_five_words, raa_sihra, raa_iram, raa_alif_ayn, raa_alif_hamza, raa_dual_alif, raa_ashiratukum, raa_wizraka, raa_dhikraka, raa_wizra_ukhra, raa_ijrami, raa_hidhrakum, raa_ibrah_kibrahu, raa_hasirat_suduruhum, raa_fathatan, raa_damma
  • Nasal: iqlab_nasal, ikhfaa_shafawi_nasal

The two readings share 12 IDs. A shared ID may carry a different default or cover different positions in each riwayah.

Contributing

If you find any issues or have feature suggestions, please open an issue or submit a pull request.

Future plans include extending the phonemizer to other riwayat, beginning with Qalun 'An Nafi' and Shu'ba 'An Asim. Contributions are welcome!

Credits

The project makes use of the Quranic Universal Library's (QUL) Quran script.

Citing

If you use this phonemizer in your work, please cite the paper as follows:

@inproceedings{
ibrahim2025quranic,
title={Qur{\textquoteright}anic Phonemizer: Bringing Tajweed-Aware Phonemes to Qur{\textquoteright}anic Machine Learning},
author={Ahmed Ibrahim},
booktitle={5th Muslims in ML Workshop co-located with NeurIPS 2025},
year={2025},
url={https://openreview.net/forum?id=hZt0JK28iV}
}

Metadata

Release files for quranic-phonemizer 3.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for quranic-phonemizer 3.0.1
File Size Uploaded
quranic_phonemizer-3.0.1.tar.gz 1.7 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for quranic-phonemizer 3.0.1
File Interpreter ABI Platform
quranic_phonemizer-3.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 3.4 MB

Release files / quranic_phonemizer-3.0.1.tar.gz

Download URL quranic_phonemizer-3.0.1.tar.gz
Size 1.7 MB
Tags Source
SHA-256 checksum
How to use checksums
d160da42f1da5eb4ed02cf9a1b6c953f2e175e7242af533d70624dbdc0596965
BLAKE2b-256 checksum
How to use checksums
1efe60c26892533c3664f85a38c01fbf3d2b12d8ec791b98b6b9dfc62f0df95e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / quranic_phonemizer-3.0.1-py3-none-any.whl

Download URL quranic_phonemizer-3.0.1-py3-none-any.whl
Size 1.7 MB
Tags Python 3
SHA-256 checksum
How to use checksums
05d34b6818c6d5f666040c5b26f4de150818966106328e5fc6fb33bb294e61cd
BLAKE2b-256 checksum
How to use checksums
1471f9db267656b7e58e1f2104a641dfd792fcdbf9d297d3d4c2ea8295a1be10
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release history Release notifications | RSS feed

3.0.5

2 release files

3.0.4

2 release files

3.0.3

2 release files

3.0.2

2 release files

This release

3.0.1 This release

2 release files

3.0

2 release files

2.15.3

2 release files

2.15.2

2 release files

2.15.1

2 release files

2.15

2 release files

2.14

2 release files

2.13

2 release files

2.12

2 release files

2.11

2 release files

2.9

2 release files

2.8

2 release files

2.7

2 release files

2.6

2 release files

2.5

2 release files

2.4

2 release files

2.3

2 release files

2.2

2 release files

2.1

2 release files

2.0

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.1

2 release files

1.0.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page