Humming to Sheet Music! 5 Hurdles Encountered in Python Web App Development and How to Overcome Them

August 17, 2025 development notes Python Webアプリ Hugging Face Gunicorn librosa

Development Image

Hello! Today, I'd like to share the real-world problem-solving journey I faced during the development of "HanautaMelody," a web application I created. This app analyzes melodies hummed by users in real-time and displays them as sheet music. While it worked perfectly in the local environment, problems arose one after another as soon as it was deployed to Hugging Face Spaces. I hope this article helps developers who are facing similar challenges.

HanautaMelody Debug 1

Hurdle 1: Server Won't Start! The Mysterious "Silent Crash"

The first problem I encountered was the most perplexing. After deployment, the app logs showed Gunicorn (the web server) attempting to start, but after a few lines of logs, it would go silent. The container would repeatedly restart without even displaying an error message. Hypothesis and Investigation: Initially, I suspected a misconfiguration of Gunicorn or an error in the Dockerfile. However, the settings were quite standard. My next suspicion was the loading of heavy libraries like librosa and music21. Libraries that handle audio and music, in particular, sometimes require system libraries written in C or other languages internally. Solution: My investigation revealed that the soundfile library, used internally by librosa, required the system library libsndfile1. It also became apparent that Hugging Face's free containers have a startup time limit, and the initial loading of libraries might be taking too long, causing a timeout. So, I modified the Dockerfile as follows:

# Before modification
RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg && rm -rf /var/lib/apt/lists/*

# After modification
RUN apt-get update && apt-get install -y --no-install-recommends \
    ffmpeg \
    libsndfile1 \
    # Add system dependencies required by the soundfile library
    && rm -rf /var/lib/apt/lists/*

# Add timeout extension option to Gunicorn startup command
CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:8000", "--timeout", "600"] # * (8000 needs to be changed individually)

With this fix, the server started successfully. Lesson: When a cloud environment malfunctions, first suspect missing system libraries.

Hurdle 2: Crash the Moment an Audio File is Uploaded

The server started, but my relief was short-lived. The moment I sent a recorded audio file to the server (/analyze), the server stopped responding again. Hypothesis and Investigation: The logs showed that the request itself reached the server. However, since the crash occurred before any Flask print() statements were executed, I inferred that the problem was happening at a very early stage of the pydub library's audio file processing. It was possible that pydub was not handling in-memory audio streams correctly in the container environment. Solution: I stopped processing directly in memory and switched to a more robust method. The uploaded audio file is first saved to disk as a temporary file on the server, and then that file path is passed to pydub for reading.

# app.py
import tempfile
import os

# ...

@app.route('/analyze', methods=['POST'])
def analyze():
    file = request.files['audio_data']

    # Create a temporary directory and file path
    temp_dir = tempfile.mkdtemp()
    temp_audio_path = os.path.join(temp_dir, 'uploaded_audio.wav')
    file.save(temp_audio_path) # Save to disk once

    try:
        # Read safely from the file path
        audio = AudioSegment.from_file(temp_audio_path)
        # ... (audio processing)
    finally:
        # Always delete temporary files after processing
        os.remove(temp_audio_path)
        os.rmdir(temp_dir)
    
    # ... (subsequent processing)

With this fix, the audio analysis process began to operate stably. Lesson: When a library behaves unstably, the old-fashioned method of going through disk can be effective.

Hurdle 3: Accuracy vs. Speed Trade-off, and Improvements

Initially, this app was planned to use Google's highly accurate pitch detection model crepe. However, this model is very heavy and would almost certainly crash due to resource limitations in a free environment. Solution: Therefore, I switched to librosa.pyin, a lightweight and fast signal processing-based algorithm. This dramatically improved analysis speed to about 3 seconds, but at the cost of some accuracy (especially octave errors). To improve this accuracy even slightly, I implemented a feature that allows users to provide hints.

  1. Add "Voice Range" selection to the UI: "Male/Low," "Female/High," "Auto"
  2. Limit analysis range in the backend: Dynamically change the frequency range (fmin, fmax) where librosa.pyin searches for pitch, according to the user's selection.
# app.py
voice_range = request.form.get('voice_range', 'auto')

if voice_range == 'female':
    fmin = librosa.note_to_hz('F3')
    fmax = librosa.note_to_hz('C6')
elif voice_range == 'male':
    fmin = librosa.note_to_hz('C2')
    fmax = librosa.note_to_hz('G4')
else: # auto
    fmin = librosa.note_to_hz('C2')
    fmax = librosa.note_to_hz('C7')

f0, voiced_flag, voiced_probs = librosa.pyin(y, fmin=fmin, fmax=fmax, sr=sr)

This small tweak prevented the algorithm from being misled by overtones and significantly improved analysis accuracy. Lesson: If the best algorithm can't be used, create a UI where humans can assist the existing algorithm to work better.

Hurdle 4: Custom Domains and iframe Traps

Hugging Face Spaces' custom domain feature was for paid plans. So, I adopted a free workaround: embedding the app in an iframe using a static hosting service like Cloudflare Pages. However, this created new problems. Problem 1: Microphone not working! When trying to use the microphone from within an iframe, it was blocked by the browser's security features, resulting in a "Permission denied" error. Solution 1: Added the allow="microphone" attribute to the iframe tag, delegating microphone usage permission from the parent page to the content within the iframe.

<iframe src="https://my-username-hum-to-score.hf.space" allow="microphone"></iframe>

Problem 2: Footer links opening within the iframe Clicking a footer link within the iframe (e.g., /privacy-policy) would cause only the iframe content to navigate, resulting in a "page not found" error. Solution 2: Added the target="_top" attribute to the <a> tag, instructing the browser to open the page in the entire browser window. Also, corrected the URL to an absolute path including the custom domain.

<a href="https://app.mydomain.com/privacy_policy.html" target="_top">Privacy Policy</a>

Lesson: iframes are convenient, but you need to understand their unique rules for security and navigation.

Hurdle 5: Data Type Bugs - The Eternal Theme of Web Development

Finally, I encountered a problem that every web developer experiences at some point: data type issues. Problem: The initial melody analysis succeeded, but pressing the Transpose or MIDI Export buttons resulted in a TypeError: unsupported operand type(s) for /: 'int' and 'str'. Cause: During the initial analysis, the BPM (tempo) sent from the HTML form was converted to a number using int(). However, for update processes like transposition, the BPM sent via JavaScript's fetch API was interpreted as a string on the Python side. The music21 library attempted to perform tempo calculations (division) with a number and a string, leading to the error. Solution: In the API endpoints that receive update requests (/update_score, /update_midi), I also properly converted the BPM to a number using int().

# app.py
@app.route('/update_score', methods=['POST'])
def update_score():
    data = request.json
    # Convert the received bpm string to a number
    bpm = int(data.get('bpm', 120)) 
    # ...

Lesson: Always convert and validate externally received data to the expected data type.

HanautaMelody Debug 2

Summary

The development of "HanautaMelody" was a continuous process of debugging and problem-solving.

By overcoming these hurdles one by one, the app became more robust and practical. I hope this development blog helps with your projects.

Please try humming to sheet music yourself! Try HanautaMelody.

← Previous Entry: International Student Recruitment Site Development in JapanNext Entry: Setting Up a Next.js Blog: Navigating Initial Git & Deployment Hurdles
← Back to Blog List