This project had nothing to do with FiveM but I thought it could be useful for developers who create creative and realistic scripts (like MDT, EMS).
Those all generated from actual-fingerprints. All grown from seeds. You do pass a string, you get a print back. The image, the type (loop / whorl / arch), and the minutiae list (ridge endings and bifurcations with angles). You don’t need any db operations underneath since same seed gives you the exact same print every time on any machine, so you don’t store images at all, you store the seed. that was the part that made me think of FiveM tbh, everyone’s database is already fat enough.
NOTE: It’s a library not a resource. there’s no fxmanifest, you npm install actual-fingerprints into your own resource and bundle it.
A little demonstration on how I’d wire it up, not saying this is the only way:
When a character gets created, server side:
import { generate } from 'actual-fingerprints'
import { seedFor } from 'actual-fingerprints/node'
const secret = GetConvar('fp_secret', '')
for (const finger of ['R1', 'R2', 'R3', 'R4', 'R5']) {
const seed = seedFor(secret, citizenId, finger)
const p = generate(seed, { hand: 'right' })
db.insert('fingerprints', { citizenid: citizenId, finger, seed, pattern: p.pattern, minutiae: JSON.stringify(p.minutiae) })
}
seedFor is just HMAC with a secret from your server config. don’t use the CitizenID raw as the seed, otherwise anyone can generate anyone’s prints on the client.
Let’s give an example; You want to show the suspect’s fingerprint in your MDT NUI, all you have to do is: send the seed over and draw it, nothing goes over the wire but a string:
import { generate, ink, toRGBA } from 'actual-fingerprints'
const p = ink(generate(seed))
ctx.putImageData(new ImageData(toRGBA(p), p.width, p.height), 0, 0)
ink() is what makes it look like it was actually pressed on paper, pores and uneven ink and sometimes a crease. leave it out for the clean version.
Matching a print from a scene against the db, the stored minutiae are enough, no regenerating:
import { compare } from 'actual-fingerprints'
const found = generate(evidenceSeed)
for (const row of db.select('fingerprints', { pattern: found.pattern })) {
if (compare(found, { minutiae: JSON.parse(row.minutiae), width: 320, height: 400 }).score > 0.3) return row.citizenid
}
Same finger scores 1, different fingers score like 0.02, so 0.3 is not a delicate number.
Only real gotcha is that generating takes ~100ms and the server runs everything on one thread. do it on character creation or in nui, not in a loop on the server, and you’ll be fine.
npm: https://www.npmjs.com/package/actual-fingerprints
source: GitHub - 1etu/actual-fingerprints: Deterministic synthetic fingerprints from a seed. Zero dependencies, Node. · GitHub
there’s a page with a live demo where you can type seeds and see what comes out, plus how it actually works if you care: actual-fingerprints: deterministic synthetic fingerprints from a seed
If you develop on Lua only? Wrap it in a tiny js server script with a couple exports, it’s like three lines. You can ask me if you get stuck.
| Code is accessible | Yes |
| Subscription-based | No |
| Lines (approximately) | 1200 |
| Requirements | Nothing |
| Support | No |

