ShadeMirror
English

Privacy Policy

Last updated: 15 October 2026


In one sentence

ShadeMirror works offline and uploads none of your data.

Try-on, rendering, face detection and saving all happen on your phone and need no network. Your photos, your saved looks and the products you add exist only on your own phone — the app never sends them anywhere.

Three places where the network is used, none of them involving your content

Three things below do produce network traffic. None of them carries your photos, your saved looks, or anything that points to you personally. We write each one down here rather than burying it in the fine print.

1. In-app purchases. Looking up the subscription price, buying and restoring are handled by the operating system's payment component (StoreKit on iOS, Google Play Billing on Android). That step needs the network, and it is the system that talks to Apple's or Google's servers. We neither initiate it nor see any of the data involved.

you "Allow ShadeMirror to use wireless data?". That prompt comes from Apple's payment component, not from our code. Declining it does not affect any part of the try-on; it only prevents subscription actions from completing.

permission (it is explicitly stripped with tools:node="remove" in the manifest). Billing goes through inter-process communication to the Google Play Store app, and that app is the one that reaches the network. On Android the app process has no networking capability at all.

2. System backup. If you have iCloud Backup (iOS) or cloud backup (Android) switched on, the operating system backs this app's data — including the photos you have saved — to your own cloud account, and restores it when you move to a new phone. That is the operating system acting on your own settings, not the app, and the data goes to your account. We still cannot see it or reach it.

We deliberately let this app's data take part in system backup — otherwise everything you have built up would be lost the moment you change phones. If you would rather it did not, you can turn backup off for this app in system settings.

3. iOS only: usage statistics from the face-detection library. We use Google's MediaPipe for face-landmark detection (the detection itself runs entirely on your device). That library ships with a piece of Google's statistics-reporting code that we cannot remove: on iOS it sends one request of about 200 bytes to play.googleapis.com roughly a minute after the app starts. The contents are the library's own operating statistics — which model was used, how many calls, how long they took, whether anything failed — plus the device model and OS version.

It cannot send your photos, and it cannot send anything that identifies you — the message format it uses has no fields for either. But it does happen, so we do not claim "zero network requests". The full measured evidence is in *About the telemetry code MediaPipe brings with it* below.

Android is not affected: with no INTERNET permission in the package, the same code cannot send anything at the operating-system level.


Why this is more than a promise

Most privacy policies open with “we care about your privacy” and then list what they collect. Ours is a different situation, and it is one you can verify:

The Android manifest does not declare the INTERNET permission.

On Android, an app without that permission cannot reach the network at the operating-system level. This is not “we promise not to send anything”; it is “it cannot”. Anyone can download the package, unpack the manifest and confirm it for themselves.

iOS has no equivalent permission mechanism. So on iOS we do not say "it cannot"; we say "we have never written a line of networking code" — which you can also verify:

grep -rn "URLSession\|CFNetwork\|NWConnection\|dataTask" ios/Sources/   # no hits

There is real network traffic on iOS, and it comes from two pieces of third-party code: Apple's own StoreKit (subscriptions), and the statistics reporting inside MediaPipe that we cannot strip out. Both are described above under *Three places where the network is used* and below under *About the telemetry code MediaPipe brings with it* — not buried in the details.


The permissions we ask for, and why

Camera

What for: showing the makeup on your face in real time.

The camera feed is processed entirely on your device: face-landmark detection (MediaPipe, running locally) and makeup rendering (your device's GPU). It never leaves the phone. Nothing is recorded or saved unless you press the shutter yourself.

Add to photo library (add-only)

What for: writing the pictures you export into the system photo library.

On iOS this is the “add-only” permission — it allows writing and forbids reading. We cannot see any other photo in your library.

Android 10 and above needs no permission at all. Android 9 and below requires the storage permission because of an OS limitation, and the manifest restricts it to exactly those older versions (maxSdkVersion="28").

The permissions we do not ask for

PermissionWhy we don't need it
Read photo libraryImporting goes through the system picker (PHPicker on iOS, Photo Picker on Android) — it runs in the system process, and we receive only the picture you selected
NetworkSee above
Location, contacts, microphone, device identifiersIrrelevant to what this app does

Face data

Face data is sensitive, so it gets its own section: what is detected, what is stored, where it is kept, how long it is retained, and who else sees it.

We do not do face recognition

What the app needs to know is where each part of the face sits in the frame — where the eyes are, where the lip line runs, where the cheekbone is — so it can decide where each layer of makeup goes.

It builds no face template capable of identifying anyone, and performs no matching, authentication, search or clustering. The data answers "where are the lips in this frame", not "who is this".

What is detected, and when anything is written to disk

During live try-on, each camera frame is passed to the MediaPipe face-landmark model running on your device (face_landmarker.task, shipped inside the app bundle, no network access at runtime), which returns a set of 2-D landmark coordinates. Those frames and coordinates exist only in memory and are discarded immediately — nothing is written to a file, nothing is uploaded.

Only when you press the shutter yourself to save a look is anything written to disk. Three things are written:

What is writtenWhy it is needed
The rendered imageThe finished picture you chose to keep
The original frame (no makeup)So that changing a shade or intensity later can be re-rendered on it
The face-landmark coordinatesSo re-rendering does not have to run detection again, and does not drift slightly each time

Where it is kept

In the app sandbox on your device, under Application Support/StyleMe/. There is no server, no account, and no copy has ever left the device.

If you have iCloud Backup (or Android cloud backup) turned on, these files are backed up along with the rest of the app's data. This is deliberate: not backing them up would mean losing everything you have built up when you change phones. The backup goes to your own cloud account — we cannot see it or reach it.

How long it is retained

You decide; we set no expiry. Delete a look and its three files are deleted immediately. Uninstalling the app deletes everything in its sandbox; copies you exported to your photo library or Files are managed by those system apps. There is no "retained for N days" policy, because we never hold this data in the first place — it stays on your device.

Whether it is shared with third parties

No. We have no server to upload it to, and we have not integrated any analytics, attribution or advertising component.

The only third-party component involved is MediaPipe (Google's open-source library). It runs on your device; it neither receives nor sends face data. The telemetry it brings with it (described above under "Three places where the network is used", item 3) reports only how many times the model was called, how long it took, the device model and the OS version — no field in that payload can hold an image or a coordinate.


Where your data lives

DataWhere it is keptWho can see it
Looks you save (rendered image + original frame + face-landmark coordinates)The app sandbox on your deviceOnly you
Products you add (brand, name, colour)The app sandbox on your deviceOnly you
Interface language, whether you've seen a tipLocal preferences on your deviceOnly you

Uninstalling the app deletes everything in its sandbox. Copies you exported to your photo library or Files are managed by those system apps. We have no server, so there is no “account data” to export or erase — because there was never an account.


Third parties

We have not added any advertising, telemetry or analytics component.

The only third-party components the app uses are:

entirely on the device

system capabilities

About the telemetry code MediaPipe brings with it

Here is one thing we cannot fully remove, stated plainly.

MediaPipe brings Google's telemetry upload code with it. The two platforms carry different copies:

MediaPipeTasksCommon — not listed as a dependency anywhere

We tried to exclude the Android one, but MediaPipe references it directly in code — with it removed, face detection will not start at all. The iOS one is inside a closed-source static library; there is not even a way to try.

On Android we have made it unable to function. On iOS we cannot. We state each side as it is.

ACCESS_NETWORK_STATE — and its three scheduling components have all been removed from the app manifest. Without the network permission it cannot open a connection at the operating-system level, and with no scheduling components there is nothing to wake it. You can verify this yourself; see the appendix.

Release build sends one request to play.googleapis.com about 60 seconds after launch (234 bytes out, 393 bytes back, HTTP 200), reproduced on three consecutive launches. The sender is Google's ClearcutLoggingClient inside the MediaPipe static library, using its bundled GTMSessionFetcher.

This is not Apple's payment component. We commented out the single StoreKit call in our own code, rebuilt, and the request still went out — so it cannot be attributed to Apple.

What it sends, and what it does not:

The message format (logs.proto.mediapipe.MediaPipeLogExtension) has only these kinds of fields: session start/end, invocation counts and durations, error codes, which model and running mode were used, and the device model and OS version.

No field in it can hold an image, a landmark coordinate, or your content — and the whole request is 234 bytes. It carries no account, no advertising identifier, nothing that points to you. We cannot read any of it either: the recipient is Google, not us. We have no server, and no Google analytics account where those numbers could be viewed.

We do not promise what we cannot deliver. The library is a closed-source static archive with no official off switch (it contains an empty TasksDummyLogger, but nothing exposes a way to select it), and our MediaPipe version is deliberately pinned — changing it means re-validating the whole face detection and camera path on real devices, which is a separate matter. So on iOS we do not claim "zero network requests".

We write all of this out because this policy asks you to verify it yourself — when you unpack the package you will find those classes, and if you capture traffic you will see that request. Better that you hear it from us than find it and start wondering what else reads like this. The appendix tells you how to reproduce it.


Payment

Purchases are completed through the App Store or Google Play. Payment details are handled by Apple or Google; we never see your payment method, card number or billing address. All we receive from the system is a single yes-or-no answer: whether this device has unlocked the full version.


Children

This app is not directed at children under 13, and it collects no age information.


Changes

If we ever introduce a networked feature (for example the product catalogue planned for a later release), we will:

  1. Say so prominently inside the app,

and leave it switched off until you agree to it

  1. Update this policy and note the revision date

We will not begin collecting data without your knowledge.


Contact

developer@metaimax.com


Appendix: how to verify the claims above

For reviewers, or anyone who would rather check for themselves.

Verify the Android build can send nothing at all:

# Unpack the build and list the permissions the manifest declares
aapt2 dump permissions app-release.apk
# Should list only CAMERA, WRITE_EXTERNAL_STORAGE (maxSdkVersion=28),
# BILLING (inter-process, not a network permission) and one app-private
# permission. There is no android.permission.INTERNET — so no code in the
# package can open a connection, the telemetry library included.

# You can also confirm its scheduling components are absent:
aapt2 dump xmltree app-release.apk --file AndroidManifest.xml | grep datatransport
# should print nothing

Verify our own code does no networking:

grep -rn "URLSession\|CFNetwork\|NWConnection\|dataTask" ios/Sources/ StyleMeCore/Sources/
grep -rn "OkHttp\|HttpURLConnection\|java.net.Socket\|Retrofit" android/app/src/main/java/

Verify the two pieces of third-party networking on iOS:

# 1. Which network-capable system frameworks the binary links
otool -L StyleMeProto.app/StyleMeProto | grep -iE "CFNetwork|Network|StoreKit"
#    You will see StoreKit.framework — Apple's payment component, as described
#    above. No CFNetwork, no third-party networking framework, no web view.

# 2. The reporting address inside MediaPipe (statically linked, so it does not
#    appear in the framework list)
strings -a StyleMeProto.app/StyleMeProto | grep play.googleapis.com
#    You will see https://play.googleapis.com/log — the request described above.

⚠️ The second command does produce output, and that is expected — we are not hiding it. It comes from the static library under Pods/MediaPipeTasksCommon/frameworks/graph_libraries/, whose symbols include mediapipe::tasks::core::logging::google_internal::ClearcutLoggingClient.

To reproduce the request: install the Release build on an iOS Simulator and watch for about 60 seconds with log stream --predicate 'process == "StyleMeProto"'. You will see hostname: play.googleapis.com and taskName = GTMSessionFetcher. That is the method we used ourselves, and where these numbers come from.

Verify the third-party dependency list: read android/app/build.gradle.kts and ios/Podfile. Both contain only MediaPipe and official system libraries.

Verify how the photo library is reached: search for PHPickerViewController (iOS) and ActivityResultContracts.PickVisualMedia (Android). Both are system pickers and require no photo-library permission.