Introduction
LaundryBro is a fun little project I built, collaborating with https://bernielabs.io/ for the mobile app. It’s a simple REST API that accepts photos of laundry labels and uses AI to tell you how to care for your clothes. It gives responses in text form or audio form, so you can get laundry tips hands-free.
Laundry Endpoints Overview
The main controller in this Spring Boot app handles a few endpoints for laundry label images.
- /laundry/text/{file}: Accepts an image file of a laundry label, sends it to an AI model (in this case, it was Gemini) for analysis, and returns a detailed text explanation and laundry care advice.
@PostMapping("/laundry/text/{file}")
public ResponseEntity<String> generateLaundryCareTextResponse(MultipartFile file) throws IOException, InterruptedException {
var base64 = Base64.getEncoder().encodeToString(file.getBytes());
return generateContent(toRequest(LAUNDRY_PROMPT, base64));
}
- /laundry/audio/{file} and /laundry/audio/async/{file}: Accept image data, generate a text response using AI, and convert it to audio using a TTS client, returning the audio as a binary stream.
@PostMapping("/laundry/audio/{file}")
public ResponseEntity<byte[]> generateLaundryCareAudioResponse(MultipartFile file) throws IOException, InterruptedException {
var base64 = Base64.getEncoder().encodeToString(file.getBytes());
var response = generateContent(toRequest(LAUNDRY_PROMPT, base64));
byte[] audioBytes = ttsClient.send(response.getBody());
return new ResponseEntity<>(audioBytes, HttpStatus.OK);
}
- /laundry/audio/async/{file}: Similar to the above, but also stores the generated audio and provides a URL for later retrieval.
@PostMapping("/laundry/audio/async/{file}")
public ResponseEntity<LaundryResponse> generateAsyncLaundryCareAudioResponse(MultipartFile file) throws IOException, InterruptedException {
var base64 = Base64.getEncoder().encodeToString(file.getBytes());
var response = generateContent(toRequest(LAUNDRY_PROMPT, base64));
byte[] audioBytes = ttsClient.send(response.getBody());
String id = toHashCode(audioBytes);
uuidToAudioMap.put(id, audioBytes);
return new ResponseEntity<>(new LaundryResponse(response.getBody(), toUrl(id)), HttpStatus.OK);
}
- /laundry/audio/{uuid}: Use the unique ID from the async endpoint to fetch your audio file later. This also helps avoid regenerating the same audio over and over.
@GetMapping("laundry/audio/{uuid}")
public ResponseEntity<byte[]> getAudio(@PathVariable String uuid) {
byte[] bytes = uuidToAudioMap.get(uuid);
return new ResponseEntity<>(bytes, HttpStatus.OK);
}
AI and TTS (text to speech)
The controller connects to Google’s Gemini model through a REST API. It sends a prompt with the image (base64-encoded), and Gemini figures out the laundry symbols and writes up some care instructions.
private ResponseEntity<String> generateContent(HttpRequest.BodyPublisher requestBody) throws IOException, InterruptedException {
var request = requestBuilder.build(URI.create(GENERATE_CONTENT_URL), requestBody);
var client = clientBuilder.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
var json = objectMapper.readTree(response.body());
var result = json.get("candidates").get(0).get("content").get("parts").get(0).get("text").asText();
return new ResponseEntity<>(result, HttpStatus.OK);
}
Request Limiter
To avoid blowing through quotas (and racking up a bill), the TTS client uses a request limiter. If the services hits the limit, it switches to a cheaper model. This keeps things fair and protects the app from abuse.
// Example usage of GoogleRequestLimitedTtsClient
private final TtsClient ttsClient = new GoogleRequestLimitedTtsClient();
Key Features
- Image Analysis: Upload a laundry label; and the AI decodes it.
- AI Explanations: Get clear, simple care instructions.
- Audio Feedback: Listen to the advice - great for multitasking.
- Caching: Audio responses are saved and can be fetched later.
- Request Limiting: Keeps usage within quotas and avoids unnecessary costs.
Close
LaundryBro is a quick demo that brings together image analysis, generative AI, and audio feedback. It’s still just a prototype (the mobile app never made it to the app store), but it shows how you can use cloud AI to make everyday stuff—like laundry—a little smarter. Maybe one day it’ll support more languages or get some new tricks!